@conform-to/zod
Advanced tools
Comparing version 0.8.0-pre.1 to 0.8.0-pre.2
@@ -1,2 +0,2 @@ | ||
import { type ZodType } from 'zod'; | ||
import { type ZodType, type ZodTypeAny, type output, ZodEffects } from 'zod'; | ||
/** | ||
@@ -13,2 +13,8 @@ * Helpers for coercing string value | ||
/** | ||
* A file schema is usually defined as `z.instanceof(File)` | ||
* which is implemented based on ZodAny with `superRefine` | ||
* Check the `instanceOfType` function on zod for more info | ||
*/ | ||
export declare function isFileSchema(schema: ZodEffects<any, any, any>): boolean; | ||
/** | ||
* @deprecated Conform coerce empty strings to undefined by default | ||
@@ -21,2 +27,2 @@ */ | ||
*/ | ||
export declare function enhanceSchema<Type>(schema: ZodType<Type>): ZodType<Type>; | ||
export declare function enableTypeCoercion<Type extends ZodTypeAny>(type: Type, cache?: Map<ZodTypeAny, ZodTypeAny>): ZodType<output<Type>>; |
151
coercion.js
@@ -37,2 +37,11 @@ 'use strict'; | ||
/** | ||
* A file schema is usually defined as `z.instanceof(File)` | ||
* which is implemented based on ZodAny with `superRefine` | ||
* Check the `instanceOfType` function on zod for more info | ||
*/ | ||
function isFileSchema(schema) { | ||
return schema._def.effect.type === 'refinement' && schema.innerType() instanceof zod.ZodAny && schema.safeParse(new File([], '')).success && !schema.safeParse('').success; | ||
} | ||
/** | ||
* @deprecated Conform coerce empty strings to undefined by default | ||
@@ -48,19 +57,20 @@ */ | ||
*/ | ||
function enhanceSchema(schema) { | ||
/** | ||
* We might be able to fix all type errors with function overloads | ||
* But I'm not sure if it's worth the effort | ||
*/ | ||
if (schema instanceof zod.ZodString || schema instanceof zod.ZodEnum || schema instanceof zod.ZodLiteral) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceString(value), schema); | ||
} else if (schema instanceof zod.ZodNumber) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceString(value, Number), schema); | ||
} else if (schema instanceof zod.ZodBoolean) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceString(value, Boolean), schema); | ||
} else if (schema instanceof zod.ZodDate) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceString(value, timestamp => { | ||
function enableTypeCoercion(type) { | ||
var cache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Map(); | ||
var result = cache.get(type); | ||
// Return the cached schema if it's already processed | ||
// This is to prevent infinite recursion caused by z.lazy() | ||
if (result) { | ||
return result; | ||
} | ||
var schema = type; | ||
if (type instanceof zod.ZodString || type instanceof zod.ZodLiteral || type instanceof zod.ZodEnum || type instanceof zod.ZodNativeEnum) { | ||
schema = zod.preprocess(value => coerceString(value), type); | ||
} else if (type instanceof zod.ZodNumber) { | ||
schema = zod.preprocess(value => coerceString(value, Number), type); | ||
} else if (type instanceof zod.ZodBoolean) { | ||
schema = zod.preprocess(value => coerceString(value, text => text === 'on' ? true : text), type); | ||
} else if (type instanceof zod.ZodDate) { | ||
schema = zod.preprocess(value => coerceString(value, timestamp => { | ||
var date = new Date(timestamp); | ||
@@ -75,9 +85,7 @@ | ||
return date; | ||
}), schema); | ||
} else if (schema instanceof zod.ZodBigInt) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceString(value, BigInt), schema); | ||
} else if (schema instanceof zod.ZodArray) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => { | ||
}), type); | ||
} else if (type instanceof zod.ZodBigInt) { | ||
schema = zod.preprocess(value => coerceString(value, BigInt), type); | ||
} else if (type instanceof zod.ZodArray) { | ||
schema = zod.preprocess(value => { | ||
// No preprocess needed if the value is already an array | ||
@@ -93,66 +101,62 @@ if (Array.isArray(value)) { | ||
return [value]; | ||
}, new zod.ZodArray(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
type: enhanceSchema(schema.element) | ||
}, new zod.ZodArray(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
type: enableTypeCoercion(type.element, cache) | ||
}))); | ||
} else if (schema instanceof zod.ZodObject) { | ||
var _shape = Object.fromEntries(Object.entries(schema.shape).map(_ref => { | ||
} else if (type instanceof zod.ZodObject) { | ||
var _shape = Object.fromEntries(Object.entries(type.shape).map(_ref => { | ||
var [key, def] = _ref; | ||
return [key, | ||
// @ts-expect-error see message above | ||
enhanceSchema(def)]; | ||
enableTypeCoercion(def, cache)]; | ||
})); | ||
return new zod.ZodObject(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
schema = new zod.ZodObject(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
shape: () => _shape | ||
})); | ||
} else if (schema instanceof zod.ZodIntersection) { | ||
// @ts-expect-error see message above | ||
return new zod.ZodIntersection(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
left: enhanceSchema(schema._def.left), | ||
right: enhanceSchema(schema._def.right) | ||
} else if (type instanceof zod.ZodEffects) { | ||
if (isFileSchema(type)) { | ||
return zod.preprocess(value => coerceFile(value), type); | ||
} | ||
schema = new zod.ZodEffects(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
schema: enableTypeCoercion(type.innerType(), cache) | ||
})); | ||
} else if (schema instanceof zod.ZodUnion) { | ||
return new zod.ZodUnion(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
options: schema.options.map(enhanceSchema) | ||
} else if (type instanceof zod.ZodOptional) { | ||
schema = zod.preprocess(value => coerceString(coerceFile(value)), new zod.ZodOptional(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
innerType: enableTypeCoercion(type.unwrap(), cache) | ||
}))); | ||
} else if (type instanceof zod.ZodDefault) { | ||
schema = new zod.ZodDefault(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
innerType: enableTypeCoercion(type.removeDefault(), cache) | ||
})); | ||
} else if (schema instanceof zod.ZodDiscriminatedUnion) { | ||
return new zod.ZodDiscriminatedUnion(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
options: schema.options.map(enhanceSchema) | ||
} else if (type instanceof zod.ZodIntersection) { | ||
schema = new zod.ZodIntersection(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
left: enableTypeCoercion(type._def.left, cache), | ||
right: enableTypeCoercion(type._def.right, cache) | ||
})); | ||
} else if (schema instanceof zod.ZodTuple) { | ||
// @ts-expect-error see message above | ||
return new zod.ZodTuple(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
items: schema.items.map(enhanceSchema) | ||
} else if (type instanceof zod.ZodUnion) { | ||
schema = new zod.ZodUnion(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
options: type.options.map(option => enableTypeCoercion(option, cache)) | ||
})); | ||
} else if (schema instanceof zod.ZodNullable) { | ||
// @ts-expect-error see message above | ||
return new zod.ZodNullable(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
innerType: enhanceSchema(schema.unwrap()) | ||
} else if (type instanceof zod.ZodDiscriminatedUnion) { | ||
schema = new zod.ZodDiscriminatedUnion(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
options: type.options.map(option => enableTypeCoercion(option, cache)) | ||
})); | ||
} else if (schema instanceof zod.ZodPipeline) { | ||
// @ts-expect-error see message above | ||
return new zod.ZodPipeline(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
in: enhanceSchema(schema._def.in) | ||
} else if (type instanceof zod.ZodTuple) { | ||
schema = new zod.ZodTuple(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
items: type.items.map(item => enableTypeCoercion(item, cache)) | ||
})); | ||
} else if (schema instanceof zod.ZodEffects) { | ||
// A file schema is usually defined as `instanceOf(File)` | ||
// which is implemented based on ZodAny with `superRefine` | ||
// You can check the `instanceOfType` function on zod for more info | ||
if (schema._def.effect.type === 'refinement' && schema.innerType() instanceof zod.ZodAny) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceFile(value), schema); | ||
} | ||
return new zod.ZodEffects(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
schema: enhanceSchema(schema.innerType()) | ||
} else if (type instanceof zod.ZodNullable) { | ||
schema = new zod.ZodNullable(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
innerType: enableTypeCoercion(type.unwrap(), cache) | ||
})); | ||
} else if (schema instanceof zod.ZodOptional) { | ||
// @ts-expect-error see message above | ||
return zod.preprocess(value => coerceString(coerceFile(value)), new zod.ZodOptional(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
innerType: enhanceSchema(schema.unwrap()) | ||
}))); | ||
} else if (schema instanceof zod.ZodDefault) { | ||
// @ts-expect-error see message above | ||
return new zod.ZodDefault(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, schema._def), {}, { | ||
innerType: enhanceSchema(schema.removeDefault()) | ||
} else if (type instanceof zod.ZodPipeline) { | ||
schema = new zod.ZodPipeline(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, type._def), {}, { | ||
in: enableTypeCoercion(type._def.in, cache), | ||
out: enableTypeCoercion(type._def.out, cache) | ||
})); | ||
} else if (type instanceof zod.ZodLazy) { | ||
schema = zod.lazy(() => enableTypeCoercion(type.schema, cache)); | ||
} | ||
if (type !== schema) { | ||
cache.set(type, schema); | ||
} | ||
return schema; | ||
@@ -163,3 +167,4 @@ } | ||
exports.coerceString = coerceString; | ||
exports.enhanceSchema = enhanceSchema; | ||
exports.enableTypeCoercion = enableTypeCoercion; | ||
exports.ifNonEmptyString = ifNonEmptyString; | ||
exports.isFileSchema = isFileSchema; |
@@ -6,3 +6,3 @@ { | ||
"license": "MIT", | ||
"version": "0.8.0-pre.1", | ||
"version": "0.8.0-pre.2", | ||
"main": "index.js", | ||
@@ -29,3 +29,3 @@ "module": "index.mjs", | ||
"peerDependencies": { | ||
"@conform-to/dom": "0.8.0-pre.1", | ||
"@conform-to/dom": "0.8.0-pre.2", | ||
"zod": "^3.21.0" | ||
@@ -32,0 +32,0 @@ }, |
@@ -12,3 +12,3 @@ 'use strict'; | ||
resolve(payload, intent) { | ||
var schema = coercion.enhanceSchema(typeof config.schema === 'function' ? config.schema(intent) : config.schema); | ||
var schema = coercion.enableTypeCoercion(typeof config.schema === 'function' ? config.schema(intent) : config.schema); | ||
var resolveResult = result => { | ||
@@ -15,0 +15,0 @@ if (result.success) { |
@@ -70,3 +70,3 @@ # @conform-to/zod | ||
```tsx | ||
import { useForm, report } from '@conform-to/react'; | ||
import { useForm } from '@conform-to/react'; | ||
import { parse } from '@conform-to/zod'; | ||
@@ -90,3 +90,3 @@ import { z } from 'zod'; | ||
if (!submission.value || submission.intent !== 'submit') { | ||
return report(submission); | ||
return submission; | ||
} | ||
@@ -116,12 +116,16 @@ | ||
.email('Email is invalid') | ||
.superRefine((email, ctx) => | ||
refine(ctx, { | ||
// It fallbacks to server validation when it returns an undefined value | ||
validate: () => constraints.isEmailUnique?.(email), | ||
// This makes it validate only when the user is submitting the form | ||
// or updating the email | ||
when: intent === 'submit' || intent === 'validate/email', | ||
message: 'Email is already used', | ||
}), | ||
// Pipe the schema so it runs only if the username is valid | ||
.pipe( | ||
z.string().superRefine((email, ctx) => | ||
refine(ctx, { | ||
// It fallbacks to server validation when it returns an undefined value | ||
validate: () => constraints.isEmailUnique?.(email), | ||
// This makes it validate only when the user is submitting the form | ||
// or updating the email | ||
when: intent === 'submit' || intent === 'validate/email', | ||
message: 'Email is already used', | ||
}), | ||
), | ||
), | ||
// ... | ||
@@ -128,0 +132,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
39613
70006
861
132
3
29