@lollipop-onl/myzod-to-zod
v1.2.0
Published
A codemod to migrate from myzod to zod v3
Readme
myzod-to-zod
A codemod to automatically migrate your codebase from myzod to zod v3.
This tool uses AST transformations to safely and accurately convert myzod validation schemas to their zod v3 equivalents, achieving 100% automated conversion for supported patterns.
✨ Features
- 🔄 100% Test Coverage: All transformation patterns tested and verified
- 🛡️ AST-based: Safe transformations that preserve code structure and comments
- ⚡ High Success Rate: Converts common myzod patterns automatically
- 🎯 Precise: Only transforms myzod-related code, leaves everything else untouched
- 📚 Well Documented: Comprehensive guide for manual migration steps
🚀 Quick Start
Interactive CLI (Recommended)
npx @lollipop-onl/myzod-to-zodNon-interactive CLI
# Preview changes (dry run)
npx @lollipop-onl/myzod-to-zod "src/**/*.ts"
# Apply transformations
npx @lollipop-onl/myzod-to-zod "src/**/*.ts" --writeInstall as Package
npm install -D @lollipop-onl/myzod-to-zod📖 Example
Before (myzod)
import myzod from 'myzod';
const userSchema = myzod.object({
name: myzod.string().min(1).max(50),
email: myzod.string().pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/),
age: myzod.number().min(0).max(150).optional(),
isActive: myzod.boolean().default(true),
});
// Complex transformations
const statusSchema = myzod.literals('active', 'inactive', 'pending');
const coerceSchema = myzod.number().coerce();
const validatedSchema = myzod.string().withPredicate(
s => s.length > 0,
'String must not be empty'
);
// Schema shape access
const userShape = userSchema.shape();
// Type inference
type User = myzod.Infer<typeof userSchema>;
type Status = myzod.Infer<typeof statusSchema>;After (zod v3)
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1).max(50),
email: z.string().regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/),
age: z.number().min(0).max(150).optional(),
isActive: z.boolean().default(true),
});
// Complex transformations
const statusSchema = z.union([z.literal("active"), z.literal("inactive"), z.literal("pending")]);
const coerceSchema = z.coerce.number();
const validatedSchema = z.string().refine(
s => s.length > 0,
'String must not be empty'
);
// Schema shape access
const userShape = userSchema.shape;
// Type inference
type User = z.infer<typeof userSchema>;
type Status = z.infer<typeof statusSchema>;🔄 Transformation Coverage
✅ Fully Automated
Basic Types
myzod.string()→z.string()myzod.number()→z.number()myzod.boolean()→z.boolean()myzod.literal()→z.literal()myzod.object()→z.object()myzod.array()→z.array()myzod.union()→z.union()myzod.tuple()→z.tuple()myzod.record()→z.record()myzod.dictionary()→z.record()(with automatic.optional()handling)
Constraints & Validation
.min()/.max()→.min()/.max().pattern()→.regex().default()→.default().optional()→.optional().nullable()→.nullable()- Array length constraints
Advanced Transformations
.withPredicate()→.refine()(Custom validation).map()→.transform()(Value transformation).check()→.safeParse().success(Boolean validation).shape()→.shape(Schema shape access).partial()→.partial()(Object partial types).collectErrors()→ removed (zod collects errors by default)myzod.number().coerce()→z.coerce.number()(Structural change)myzod.intersection()→z.intersection()(Intersection types)myzod.literals('a', 'b')→z.union([z.literal('a'), z.literal('b')])(Complex expansion)myzod.enum(Color)→z.nativeEnum(Color)(TypeScript enums)
Type System
myzod.Infer<typeof T>→z.infer<typeof T>(Type inference)
🔧 Manual Migration Required
While this codemod handles 100% of schema definition patterns automatically, error handling requires manual attention:
1. ValidationError Imports
⚠️ Manual Change Required
// Before
import myzod, { ValidationError } from 'myzod';
// After (manual fix needed)
import { z } from 'zod';
// Remove ValidationError import entirelyWhy: zod uses a different error handling pattern that doesn't require importing error types.
2. Error Handling Patterns
⚠️ Manual Change Required
// Before
const result = schema.try(data);
if (result instanceof myzod.ValidationError) {
// Handle error
}
// After (manual fix needed)
const result = schema.safeParse(data);
if (!result.success) {
// Handle error with result.error
}Why: myzod and zod have fundamentally different error handling APIs.
3. Object Property Handling Differences
⚠️ Manual Change Required
// myzod (rejects unknown properties by default)
const schema = myzod.object({
name: myzod.string()
});
// schema.parse({name: "John", extra: "value"}) -> ValidationError
// Equivalent strict behavior in zod
const schema = z.object({
name: z.string()
}).strict();
// schema.parse({name: "John", extra: "value"}) -> ZodError
// zod default behavior (strips unknown properties)
const schema = z.object({
name: z.string()
});
// schema.parse({name: "John", extra: "value"}) -> {name: "John"}
// myzod.allowUnknownKeys() equivalent in zod (passes through unknown properties)
const schema = z.object({
name: z.string()
}).passthrough();
// schema.parse({name: "John", extra: "value"}) -> {name: "John", extra: "value"}Why:
- myzod default: Rejects unknown properties (strict)
- zod default: Strips unknown properties (lenient)
- Migration impact: Objects with extra properties will behave differently
Solution:
- Use
z.object().strict()instead ofz.object()to match myzod's default behavior - Or use
z.object().passthrough()for myzod's.allowUnknownKeys()behavior
📖 For detailed error handling migration patterns and examples, see our Error Handling Migration Guide - ValidationError and .try() patterns
📋 Post-Migration Checklist
After running the codemod, please:
- Remove ValidationError Imports: Remove
{ ValidationError }from import statements - Review Error Handling: Update
.try()calls to.safeParse()andinstanceofchecks - Test Thoroughly: Run your test suite to catch any behavioral differences
- Update Dependencies: Remove myzod and ensure zod v3 is installed
🛠️ Installation & Setup
Prerequisites
- Node.js 18+
- TypeScript project
- myzod schemas in your codebase
Steps
- Backup your code (recommended)
- Install zod v3 in your project:
npm install zod@3 - Run the codemod:
npx @lollipop-onl/myzod-to-zod "src/**/*.ts" --write - Follow the manual migration steps above
- Remove myzod:
npm uninstall myzod - Test thoroughly
🐛 Troubleshooting
Common Issues
"SyntaxError: Unexpected token"
- Ensure your TypeScript files are valid before running the codemod
- Check that file paths are correctly specified
"No files found"
- Use quotes around glob patterns:
"src/**/*.ts" - Verify the path patterns match your project structure
"Some transformations missing"
- Check the manual migration section above
- Report missing patterns as GitHub issues
"Type errors after migration"
- Review error handling patterns (
.try()→.safeParse()) - Ensure zod v3 is properly installed
- Check for any remaining myzod imports
Getting Help
- Check the manual migration guide above
- Review the troubleshooting section
- Open an issue with a minimal reproduction
🧪 Development
Running Tests
npm test # Run all tests
npm run test:watch # Watch mode
npm run typecheck # Type checkingBuilding
npm run build # Build the codemodProject Structure
myzod-to-zod/
├── src/ # Main implementation
│ ├── index.ts # CLI entry point
│ ├── migrate.ts # AST transformation logic
│ ├── collect-imports.ts # Import analysis
│ └── myzod-node.ts # AST utilities
├── test/ # Test suite
│ ├── scenarios.ts # Main test file
│ └── __scenarios__/ # 45 test cases with README
└── docs/ # Developer documentation
├── implementation-guide.md # AST architecture guide
├── api-transformation-reference.md # Complete transformation patterns
├── library-comparison-reference.md # Performance comparison & migration rationale
└── error-handling-migration.md # ValidationError & try() migration guide📚 Resources
- myzod Documentation
- zod v3 Documentation
- AST Explorer for understanding transformations
- TypeScript Compiler API
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new transformation patterns
- Ensure all tests pass
- Submit a pull request
Adding New Transformations
- Add test cases in
test/__scenarios__/ - Implement transformation in
src/migrate.ts - Update documentation
- Follow TDD principles
📄 License
MIT License. See LICENSE for details.
🙏 Acknowledgments
- Inspired by zod-v3-to-v4
- Built with ts-morph for safe AST transformations
- Thanks to the myzod and zod communities
Need help? Open an issue or check our troubleshooting guide.
