function transform(file, api, options) { const j = api.jscodeshift; const root = j(file.source); // Helper: replaces deprecated error params with new unified "error" function replaceErrorParams(args) { return args.map(arg => { if (!j.ObjectExpression.check(arg)) return arg; const newProps = []; let requiredError, invalidTypeError, messageProp, errorMap; arg.properties.forEach(prop => { if (j.Property.check(prop) && j.Identifier.check(prop.key)) { const keyName = prop.key.name; if (keyName === 'required_error') requiredError = prop.value; else if (keyName === 'invalid_type_error') invalidTypeError = prop.value; else if (keyName === 'message') messageProp = prop.value; else if (keyName === 'errorMap') errorMap = prop.value; else newProps.push(prop); } else { newProps.push(prop); } }); if (requiredError || invalidTypeError) { const errorFn = j.arrowFunctionExpression( [j.identifier('issue')], j.blockStatement([ j.ifStatement( j.binaryExpression( '===', j.memberExpression(j.identifier('issue'), j.identifier('input')), j.identifier('undefined') ), j.blockStatement([j.returnStatement(requiredError || j.literal(""))]), invalidTypeError ? j.blockStatement([j.returnStatement(invalidTypeError)]) : null ) ].filter(Boolean)) ); newProps.push(j.property('init', j.identifier('error'), errorFn)); } else if (messageProp) { newProps.push(j.property('init', j.identifier('error'), messageProp)); } else if (errorMap) { newProps.push(j.property('init', j.identifier('error'), j.arrowFunctionExpression( [j.identifier('issue')], j.blockStatement([ j.ifStatement( j.binaryExpression( '===', j.memberExpression(j.identifier('issue'), j.identifier('code')), j.literal('too_small') ), j.blockStatement([ j.returnStatement( j.templateLiteral([ j.templateElement({ raw: 'Value must be >', cooked: 'Value must be >' }), j.templateElement({ raw: '', cooked: '' }, true) ], [j.memberExpression(j.identifier('issue'), j.identifier('minimum'))]) ) ]), j.blockStatement([j.returnStatement(j.literal(undefined))]) ) ]) ))); } return j.objectExpression(newProps); }); } // Replace deprecated .strict(), .passthrough(), .strip() root .find(j.CallExpression) .forEach(path => { const callee = path.node.callee; if (j.MemberExpression.check(callee) && j.Identifier.check(callee.property)) { const method = callee.property.name; const object = callee.object; if (method === 'strict') { path.replace(j.callExpression( j.identifier('z.strictObject'), object.arguments || [object] )); } else if (method === 'passthrough') { path.replace(j.callExpression( j.identifier('z.looseObject'), object.arguments || [object] )); } else if (method === 'strip') { // z.object() defaults to strip, so drop it path.replace(object); } } }); // Replace deprecated z.string().email() with z.email() root .find(j.CallExpression) .forEach(path => { const callee = path.node.callee; if ( j.MemberExpression.check(callee) && j.Identifier.check(callee.property) && ['email', 'uuid', 'url', 'emoji', 'base64', 'base64url', 'nanoid', 'cuid', 'cuid2', 'ulid', 'ipv4', 'ipv6', 'cidr'].includes(callee.property.name) ) { path.replace(j.callExpression(j.identifier(`z.${callee.property.name}`), [])); } }); // Update method call options e.g. .min(5, { message: ... }) root .find(j.CallExpression) .forEach(path => { const callee = path.node.callee; if (j.MemberExpression.check(callee)) { const args = path.node.arguments; if (args.length > 0) { path.node.arguments = replaceErrorParams(args); } } }); // Update top-level schema definitions like z.string({ ... }) root .find(j.CallExpression, { callee: { type: 'Identifier', name: 'string' } }) .forEach(path => { if (path.node.arguments.length > 0) { path.node.arguments = replaceErrorParams(path.node.arguments); } }); return root.toSource(options); }; export default transform;
Input
import { z } from 'zod'; // 1. message -> error const schema1 = z.string().min(5, { message: 'Too short.' }); // 2. required_error and invalid_type_error -> error function const schema2 = z.string({ required_error: 'This field is required', invalid_type_error: 'Not a string', }); // 3. errorMap -> error function const schema3 = z.string({ errorMap: (issue, ctx) => { if (issue.code === 'too_small') { return { message: `Value must be >${issue.minimum}` }; } return { message: ctx.defaultError }; }, }); // 4. ZodError instanceof try { z.string().parse(123); } catch (err) { if (err instanceof z.ZodError) { console.log('Caught a ZodError'); } } // 5. Deprecated .strict(), .passthrough(), .strip() const obj1 = z.object({ name: z.string() }).strict(); const obj2 = z.object({ name: z.string() }).passthrough(); const obj3 = z.object({ name: z.string() }).strip(); // 6. Deprecated z.string().email() etc const schema4 = z.string().email(); const schema5 = z.string().uuid(); const schema6 = z.string().url(); const schema7 = z.string().ipv4(); const schema8 = z.string().cidr(); // 7. z.record() single argument const rec1 = z.record(z.string()); // 8. z.literal(symbol) const sym = Symbol('s'); const symLit = z.literal(sym); // 9. z.promise const asyncSchema = z.promise(z.string()); // 10. z.function() const funcSchema = z.function() .args(z.string()) .returns(z.number()); // 11. z.unknown() optionality difference const unknownSchema = z.object({ a: z.any(), b: z.unknown() }); // 12. .nonempty() const arr = z.array(z.string()).nonempty(); // 13. .refine() with type predicate const predicateRefine = z.unknown().refine((val): val is string => typeof val === 'string');
Output
loading
Read-only