zod-v3-to-v4 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![NPM version](https://img.shields.io/npm/v/zod-v3-to-v4.svg?style=for-the-badge)](https://www.npmjs.com/package/zod-v3-to-v4)
4
4
  [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nicoespeon/zod-v3-to-v4/ci.yml?style=for-the-badge)](https://github.com/nicoespeon/zod-v3-to-v4/actions/workflows/ci.yml)
5
+ [![All Contributors](https://img.shields.io/github/all-contributors/nicoespeon/zod-v3-to-v4?color=ee8449&style=for-the-badge)](#contributors)
5
6
 
6
7
  This is a [codemod](https://martinfowler.com/articles/codemods-api-refactoring.html) (a tool that automatically transforms code) for migrating from [Zod](https://zod.dev/) v3 to v4.
7
8
 
@@ -13,7 +14,7 @@ The migration guide can be found at <https://zod.dev/v4/changelog>
13
14
 
14
15
  > This assumes you have [Node.js](https://nodejs.org/) installed.
15
16
 
16
- The first step for you is to upgrade your Zod version to v4. In doubt, check your `package.json`.
17
+ The first step for you is to upgrade your Zod version to v4. In doubt, check your `package.json`. Note that if you're using Zod through Astro v6, you don't need to upgrade Zod manually.
17
18
 
18
19
  Then, you can use this codemod to automatically migrate your code from Zod v3 to v4 syntax.
19
20
 
@@ -92,3 +93,58 @@ Or interactively:
92
93
  pnpm playground:interactive
93
94
  # Then enter: playground/tsconfig.json
94
95
  ```
96
+
97
+ ## Contributors
98
+
99
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
100
+ <!-- prettier-ignore-start -->
101
+ <!-- markdownlint-disable -->
102
+ <table>
103
+ <tbody>
104
+ <tr>
105
+ <td align="center" valign="top" width="14.28%"><a href="https://nicoespeon.com/"><img src="https://avatars0.githubusercontent.com/u/1094774?v=4?s=100" width="100px;" alt="Nicolas Carlo"/><br /><sub><b>Nicolas Carlo</b></sub></a><br /><a href="#ideas-nicoespeon" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/nicoespeon/zod-v3-to-v4/commits?author=nicoespeon" title="Code">💻</a> <a href="https://github.com/nicoespeon/zod-v3-to-v4/commits?author=nicoespeon" title="Documentation">📖</a> <a href="https://github.com/nicoespeon/zod-v3-to-v4/pulls?q=is%3Apr+reviewed-by%3Anicoespeon" title="Reviewed Pull Requests">👀</a> <a href="#question-nicoespeon" title="Answering Questions">💬</a></td>
106
+ <td align="center" valign="top" width="14.28%"><a href="https://hideoo.dev"><img src="https://avatars.githubusercontent.com/u/494699?v=4?s=100" width="100px;" alt="HiDeoo"/><br /><sub><b>HiDeoo</b></sub></a><br /><a href="#ideas-HiDeoo" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/nicoespeon/zod-v3-to-v4/commits?author=HiDeoo" title="Code">💻</a></td>
107
+ <td align="center" valign="top" width="14.28%"><a href="https://contra.com"><img src="https://avatars.githubusercontent.com/u/973543?v=4?s=100" width="100px;" alt="Gajus Kuizinas"/><br /><sub><b>Gajus Kuizinas</b></sub></a><br /><a href="#ideas-gajus" title="Ideas, Planning, & Feedback">🤔</a></td>
108
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Loskir"><img src="https://avatars.githubusercontent.com/u/21295738?v=4?s=100" width="100px;" alt="Kirill Loskutov"/><br /><sub><b>Kirill Loskutov</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3ALoskir" title="Bug reports">🐛</a></td>
109
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Schrubitteflau"><img src="https://avatars.githubusercontent.com/u/45637360?v=4?s=100" width="100px;" alt="Schrubitteflau"/><br /><sub><b>Schrubitteflau</b></sub></a><br /><a href="#ideas-Schrubitteflau" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/nicoespeon/zod-v3-to-v4/commits?author=Schrubitteflau" title="Code">💻</a></td>
110
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/umbrellasalesman02"><img src="https://avatars.githubusercontent.com/u/10819467?v=4?s=100" width="100px;" alt="Erik"/><br /><sub><b>Erik</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Aumbrellasalesman02" title="Bug reports">🐛</a></td>
111
+ <td align="center" valign="top" width="14.28%"><a href="http://www.einargudni.com"><img src="https://avatars.githubusercontent.com/u/17381009?v=4?s=100" width="100px;" alt="Einar Guðni Guðjónsson"/><br /><sub><b>Einar Guðni Guðjónsson</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Aeinargudnig" title="Bug reports">🐛</a></td>
112
+ </tr>
113
+ <tr>
114
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/DanUgbeye"><img src="https://avatars.githubusercontent.com/u/82518175?v=4?s=100" width="100px;" alt="Daniel Ugbeye"/><br /><sub><b>Daniel Ugbeye</b></sub></a><br /><a href="#ideas-DanUgbeye" title="Ideas, Planning, & Feedback">🤔</a></td>
115
+ <td align="center" valign="top" width="14.28%"><a href="https://firxworx.com"><img src="https://avatars.githubusercontent.com/u/24518234?v=4?s=100" width="100px;" alt="Kevin Firko"/><br /><sub><b>Kevin Firko</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Afirxworx" title="Bug reports">🐛</a></td>
116
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/greg-sims"><img src="https://avatars.githubusercontent.com/u/110814147?v=4?s=100" width="100px;" alt="Greg Sims"/><br /><sub><b>Greg Sims</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Agreg-sims" title="Bug reports">🐛</a></td>
117
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/PowerSupply"><img src="https://avatars.githubusercontent.com/u/622851?v=4?s=100" width="100px;" alt="PowerSupply"/><br /><sub><b>PowerSupply</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3APowerSupply" title="Bug reports">🐛</a></td>
118
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/cfrerebeau"><img src="https://avatars.githubusercontent.com/u/768210?v=4?s=100" width="100px;" alt="Christophe Frèrebeau"/><br /><sub><b>Christophe Frèrebeau</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Acfrerebeau" title="Bug reports">🐛</a></td>
119
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/aelligsen"><img src="https://avatars.githubusercontent.com/u/210378474?v=4?s=100" width="100px;" alt="aelligsen"/><br /><sub><b>aelligsen</b></sub></a><br /><a href="#ideas-aelligsen" title="Ideas, Planning, & Feedback">🤔</a></td>
120
+ <td align="center" valign="top" width="14.28%"><a href="http://nikmaxott.org"><img src="https://avatars.githubusercontent.com/u/8322965?v=4?s=100" width="100px;" alt="Nikolaus Z"/><br /><sub><b>Nikolaus Z</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Anikmaxott" title="Bug reports">🐛</a></td>
121
+ </tr>
122
+ <tr>
123
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/bj0rge"><img src="https://avatars.githubusercontent.com/u/3996102?v=4?s=100" width="100px;" alt="Bastien Jorge"/><br /><sub><b>Bastien Jorge</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Abj0rge" title="Bug reports">🐛</a></td>
124
+ <td align="center" valign="top" width="14.28%"><a href="https://danf.ca/"><img src="https://avatars.githubusercontent.com/u/53399?v=4?s=100" width="100px;" alt="Daniel Friesen"/><br /><sub><b>Daniel Friesen</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Adantman" title="Bug reports">🐛</a></td>
125
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/affeldt28"><img src="https://avatars.githubusercontent.com/u/45835050?v=4?s=100" width="100px;" alt="Marvin Affeldt"/><br /><sub><b>Marvin Affeldt</b></sub></a><br /><a href="#ideas-affeldt28" title="Ideas, Planning, & Feedback">🤔</a></td>
126
+ <td align="center" valign="top" width="14.28%"><a href="http://aqeelat.com"><img src="https://avatars.githubusercontent.com/u/5212744?v=4?s=100" width="100px;" alt="Abdullah Alaqeel"/><br /><sub><b>Abdullah Alaqeel</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Aaqeelat" title="Bug reports">🐛</a></td>
127
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/wonderWoman1408"><img src="https://avatars.githubusercontent.com/u/5628562?v=4?s=100" width="100px;" alt="Wonder Woman"/><br /><sub><b>Wonder Woman</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3AwonderWoman1408" title="Bug reports">🐛</a></td>
128
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/nzhiti"><img src="https://avatars.githubusercontent.com/u/57987532?v=4?s=100" width="100px;" alt="nzhiti"/><br /><sub><b>nzhiti</b></sub></a><br /><a href="#ideas-nzhiti" title="Ideas, Planning, & Feedback">🤔</a></td>
129
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/lyricnz"><img src="https://avatars.githubusercontent.com/u/122371?v=4?s=100" width="100px;" alt="Simon Roberts"/><br /><sub><b>Simon Roberts</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Alyricnz" title="Bug reports">🐛</a></td>
130
+ </tr>
131
+ <tr>
132
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/yann-combarnous"><img src="https://avatars.githubusercontent.com/u/39089766?v=4?s=100" width="100px;" alt="yann-combarnous"/><br /><sub><b>yann-combarnous</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Ayann-combarnous" title="Bug reports">🐛</a></td>
133
+ <td align="center" valign="top" width="14.28%"><a href="http://adam.doussan.info"><img src="https://avatars.githubusercontent.com/u/26745150?v=4?s=100" width="100px;" alt="acdoussan"/><br /><sub><b>acdoussan</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Aacdoussan" title="Bug reports">🐛</a></td>
134
+ </tr>
135
+ </tbody>
136
+ <tfoot>
137
+ <tr>
138
+ <td align="center" size="13px" colspan="7">
139
+ <img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg">
140
+ <a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
141
+ </img>
142
+ </td>
143
+ </tr>
144
+ </tfoot>
145
+ </table>
146
+
147
+ <!-- markdownlint-restore -->
148
+ <!-- prettier-ignore-end -->
149
+
150
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
@@ -1,16 +1,30 @@
1
1
  import { SyntaxKind } from "ts-morph";
2
+ export const AstroZodModuleSpecifier = "astro/zod";
3
+ // https://v6.docs.astro.build/en/guides/upgrade-to/v6/#deprecated-astroschema-and-z-from-astrocontent
4
+ export const AstroDeprecatedZodModuleSpecifiers = [
5
+ "astro:content",
6
+ "astro:schema",
7
+ ];
8
+ export const AstroZodModuleSpecifiers = [
9
+ AstroZodModuleSpecifier,
10
+ ...AstroDeprecatedZodModuleSpecifiers,
11
+ ];
12
+ const zodModuleSpecifiers = ["zod", "zod/v3", ...AstroZodModuleSpecifiers];
2
13
  export function collectZodImportDeclarations(sourceFile) {
3
14
  return sourceFile
4
15
  .getImportDeclarations()
5
- .filter((importDeclaration) => ["zod", "zod/v3"].includes(importDeclaration.getModuleSpecifierValue()));
16
+ .filter((importDeclaration) => zodModuleSpecifiers.includes(importDeclaration.getModuleSpecifierValue()));
6
17
  }
7
18
  export function getZodName(importDeclarations) {
8
- const zodImport = importDeclarations[0]
9
- ?.getNamedImports()
10
- .find((namedImport) => namedImport.getName() === "z");
19
+ const zodImport = getZodImport(importDeclarations[0]);
11
20
  const zodImportNode = zodImport?.getAliasNode() ?? zodImport?.getNameNode();
12
21
  return zodImportNode?.getText() ?? "z";
13
22
  }
23
+ export function getZodImport(importDeclaration) {
24
+ return importDeclaration
25
+ ?.getNamedImports()
26
+ .find((namedImport) => namedImport.getName() === "z");
27
+ }
14
28
  export function collectZodReferences(importDeclarations) {
15
29
  return importDeclarations.flatMap((importDeclaration) => importDeclaration.getNamedImports().flatMap((namedImport) => {
16
30
  const namedNode = namedImport.getAliasNode() ?? namedImport.getNameNode();
@@ -0,0 +1,31 @@
1
+ import { AstroDeprecatedZodModuleSpecifiers, AstroZodModuleSpecifier, getZodImport, } from "./collect-imports.js";
2
+ export function convertAstroDeprecatedZodImports(importDeclarations) {
3
+ for (const declaration of importDeclarations) {
4
+ const zodImport = getZodImport(declaration);
5
+ if (!zodImport || !isDeprecatedAstroZodImport(declaration)) {
6
+ continue;
7
+ }
8
+ // Get the alias if any before removing the import.
9
+ const alias = zodImport.getAliasNode()?.getText();
10
+ // Remove the deprecated Astro Zod import.
11
+ zodImport.remove();
12
+ if (
13
+ // If no named imports remain…
14
+ declaration.getNamedImports().length === 0 &&
15
+ // And there is no default import…
16
+ !declaration.getDefaultImport()) {
17
+ // Remove the entire import declaration.
18
+ declaration.remove();
19
+ }
20
+ // Add a new import declaration for `astro/zod`.
21
+ declaration.getSourceFile().addImportDeclaration({
22
+ moduleSpecifier: AstroZodModuleSpecifier,
23
+ namedImports: [
24
+ { name: "z", alias: alias && alias !== "z" ? alias : undefined },
25
+ ],
26
+ });
27
+ }
28
+ }
29
+ function isDeprecatedAstroZodImport(declaration) {
30
+ return AstroDeprecatedZodModuleSpecifiers.includes(declaration.getModuleSpecifierValue());
31
+ }
package/dist/migrate.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SyntaxKind } from "ts-morph";
2
2
  import { findRootExpression } from "./ast.js";
3
- import { collectZodImportDeclarations, collectZodReferences, getZodName, } from "./collect-imports.js";
3
+ import { AstroZodModuleSpecifiers, collectZodImportDeclarations, collectZodReferences, getZodName, } from "./collect-imports.js";
4
+ import { convertAstroDeprecatedZodImports } from "./convert-astro-imports.js";
4
5
  import { convertZArrayPatternsToTopLevelApi, convertZCoercePatternsToTopLevelApi, convertZFunctionPatternsToTopLevelApi, convertZNumberPatternsToZInt, convertZObjectPatternsToTopLevelApi, convertZRecordPatternsToTopLevelApi, convertZStringPatternsToTopLevelApi, } from "./convert-name-to-top-level-api.js";
5
6
  import { convertDeprecatedErrorKeysToErrorFunction, convertErrorMapToErrorFunction, convertMessageKeyToError, convertZodErrorAddIssueToDirectPushes, convertZodErrorToTreeifyError, } from "./convert-zod-errors.js";
6
7
  import { replaceDeletedTypes } from "./replace-deleted-types.js";
@@ -9,13 +10,18 @@ export function migrateZodV3ToV4(sourceFile, options = {}) {
9
10
  const importDeclarations = collectZodImportDeclarations(sourceFile);
10
11
  const zodName = getZodName(importDeclarations);
11
12
  importDeclarations.forEach((declaration) => {
12
- if (declaration.getModuleSpecifier().getText() === "zod/v3") {
13
+ const specifier = declaration.getModuleSpecifierValue();
14
+ if (specifier === "zod/v3") {
13
15
  declaration.setModuleSpecifier("zod");
14
16
  }
15
17
  if (options.migrateImportDeclarations) {
16
18
  // Use `zod/v4` explicit import. Not needed for an actual migration.
17
19
  // Useful for testing, so we can have v3.25 and typecheck both v3 and v4
18
- declaration.setModuleSpecifier("zod/v4");
20
+ // Astro re-exports don't need to be updated and don't have a v4
21
+ // dedicated export.
22
+ if (!AstroZodModuleSpecifiers.includes(specifier)) {
23
+ declaration.setModuleSpecifier("zod/v4");
24
+ }
19
25
  }
20
26
  });
21
27
  // Collect references before modifying imports
@@ -50,6 +56,7 @@ export function migrateZodV3ToV4(sourceFile, options = {}) {
50
56
  convertZodErrorToTreeifyError(sourceFile, zodName);
51
57
  convertZodErrorAddIssueToDirectPushes(sourceFile, zodName);
52
58
  renameZSchemaEnumToLowercase(sourceFile, zodName);
59
+ convertAstroDeprecatedZodImports(importDeclarations);
53
60
  return sourceFile.getFullText();
54
61
  }
55
62
  function renameZDefaultToZPrefault(node) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-v3-to-v4",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Migrate Zod from v3 to v4",
5
5
  "keywords": [
6
6
  "zod",
@@ -52,6 +52,7 @@
52
52
  "lint-staged": "16.0.0",
53
53
  "prettier": "3.5.3",
54
54
  "prettier-plugin-curly": "0.3.1",
55
+ "prettier-plugin-organize-imports": "4.3.0",
55
56
  "prettier-plugin-packagejson": "2.5.10",
56
57
  "prettier-plugin-sh": "0.15.0",
57
58
  "rimraf": "6.0.1",