zod-v3-to-v4 1.10.0 â 1.11.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 +1 -0
- package/dist/collect-imports.js +45 -1
- package/dist/convert-name-to-top-level-api.js +4 -1
- package/dist/index.js +62 -1
- package/dist/migrate.js +3 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -132,6 +132,7 @@ pnpm playground:interactive
|
|
|
132
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
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
134
|
<td align="center" valign="top" width="14.28%"><a href="http://matsjfunke.com"><img src="https://avatars.githubusercontent.com/u/125814808?v=4?s=100" width="100px;" alt="Mats Julius Funke"/><br /><sub><b>Mats Julius Funke</b></sub></a><br /><a href="#ideas-matsjfunke" title="Ideas, Planning, & Feedback">đ¤</a></td>
|
|
135
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/popfendi"><img src="https://avatars.githubusercontent.com/u/84185235?v=4?s=100" width="100px;" alt="popfendi"/><br /><sub><b>popfendi</b></sub></a><br /><a href="https://github.com/nicoespeon/zod-v3-to-v4/issues?q=author%3Apopfendi" title="Bug reports">đ</a></td>
|
|
135
136
|
</tr>
|
|
136
137
|
</tbody>
|
|
137
138
|
<tfoot>
|
package/dist/collect-imports.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SyntaxKind } from "ts-morph";
|
|
1
|
+
import { SyntaxKind, } from "ts-morph";
|
|
2
2
|
export const AstroZodModuleSpecifier = "astro/zod";
|
|
3
3
|
// https://v6.docs.astro.build/en/guides/upgrade-to/v6/#deprecated-astroschema-and-z-from-astrocontent
|
|
4
4
|
export const AstroDeprecatedZodModuleSpecifiers = [
|
|
@@ -36,3 +36,47 @@ export function collectZodReferences(importDeclarations) {
|
|
|
36
36
|
.filter((node) => node !== namedNode);
|
|
37
37
|
}));
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Collect references to variables that are derived from Zod schemas.
|
|
41
|
+
*
|
|
42
|
+
* For example, given:
|
|
43
|
+
*
|
|
44
|
+
* const baseSchema = z.string().refine(...);
|
|
45
|
+
* const constrainedSchema = baseSchema.refine(...);
|
|
46
|
+
*
|
|
47
|
+
* This function will return references to `baseSchema` (like the one used
|
|
48
|
+
* in `constrainedSchema`), so we can also migrate those.
|
|
49
|
+
*/
|
|
50
|
+
export function collectDerivedZodSchemaReferences(zodReferences) {
|
|
51
|
+
const derivedReferences = [];
|
|
52
|
+
const visited = new Set();
|
|
53
|
+
function collectFromReferences(refs) {
|
|
54
|
+
for (const ref of refs) {
|
|
55
|
+
// Find the variable declaration that contains this Zod reference
|
|
56
|
+
const variableDeclaration = ref.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
|
|
57
|
+
if (!variableDeclaration) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const variableName = variableDeclaration.getName();
|
|
61
|
+
if (visited.has(variableName)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
visited.add(variableName);
|
|
65
|
+
// Get the identifier node for the variable name
|
|
66
|
+
const nameNode = variableDeclaration.getNameNode();
|
|
67
|
+
if (!nameNode.isKind(SyntaxKind.Identifier)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// Find all references to this variable (excluding the declaration itself)
|
|
71
|
+
const variableReferences = nameNode
|
|
72
|
+
.findReferencesAsNodes()
|
|
73
|
+
.filter((node) => node !== nameNode)
|
|
74
|
+
.filter((node) => node.isKind(SyntaxKind.Identifier));
|
|
75
|
+
derivedReferences.push(...variableReferences);
|
|
76
|
+
// Recursively collect references from these derived schemas
|
|
77
|
+
collectFromReferences(variableReferences);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
collectFromReferences(zodReferences);
|
|
81
|
+
return derivedReferences;
|
|
82
|
+
}
|
|
@@ -123,7 +123,10 @@ export function convertZArrayPatternsToTopLevelApi(node, zodName) {
|
|
|
123
123
|
getCallExpressionsToConvert(node, {
|
|
124
124
|
oldName,
|
|
125
125
|
names,
|
|
126
|
-
})
|
|
126
|
+
})
|
|
127
|
+
// Start from the deepest, otherwise we can't get info from removed nodes
|
|
128
|
+
.reverse()
|
|
129
|
+
.forEach((callExpression) => {
|
|
127
130
|
// Remove deprecated names from the chain
|
|
128
131
|
callExpression
|
|
129
132
|
.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { cancel, confirm, intro, isCancel, log, outro, progress, text, } from "@clack/prompts";
|
|
3
3
|
import { exec } from "node:child_process";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
5
6
|
import { promisify } from "node:util";
|
|
6
7
|
import { Project } from "ts-morph";
|
|
8
|
+
import z from "zod";
|
|
7
9
|
import { migrateZodV3ToV4 } from "./migrate.js";
|
|
8
10
|
const execAsync = promisify(exec);
|
|
9
11
|
intro(`đī¸ Let's migrate Zod from v3 to v4`);
|
|
@@ -56,7 +58,26 @@ if (isCancel(tsConfigFilePath)) {
|
|
|
56
58
|
await runMigration(tsConfigFilePath);
|
|
57
59
|
async function runMigration(tsConfigFilePath) {
|
|
58
60
|
const project = new Project({ tsConfigFilePath });
|
|
59
|
-
|
|
61
|
+
let filesToProcess = project.getSourceFiles();
|
|
62
|
+
const references = getProjectReferences(tsConfigFilePath);
|
|
63
|
+
// If we can only find refs, we are probably in a monorepo setup.
|
|
64
|
+
// Ask the user if they want to migrate all the references.
|
|
65
|
+
const isMonorepoRoot = filesToProcess.length === 0 && references.length > 0;
|
|
66
|
+
if (isMonorepoRoot) {
|
|
67
|
+
const shouldFollowRefs = await confirm({
|
|
68
|
+
message: `Found ${references.length} project reference(s). Migrate all referenced packages?`,
|
|
69
|
+
});
|
|
70
|
+
if (isCancel(shouldFollowRefs)) {
|
|
71
|
+
cancel("Migration cancelled.");
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
if (shouldFollowRefs) {
|
|
75
|
+
for (const refPath of references) {
|
|
76
|
+
project.addSourceFilesFromTsConfig(refPath);
|
|
77
|
+
}
|
|
78
|
+
filesToProcess = project.getSourceFiles();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
60
81
|
let processedFilesCount = 0;
|
|
61
82
|
const progressBar = progress({ max: filesToProcess.length });
|
|
62
83
|
progressBar.start("Processing files...");
|
|
@@ -85,6 +106,46 @@ async function runMigration(tsConfigFilePath) {
|
|
|
85
106
|
|
|
86
107
|
âšī¸ If the migration missed something or did something wrong, please report it at https://github.com/nicoespeon/zod-v3-to-v4/issues`);
|
|
87
108
|
}
|
|
109
|
+
function getProjectReferences(tsConfigFilePath) {
|
|
110
|
+
// Use Zod to parse the tsconfig.json file for interesting fields.
|
|
111
|
+
const tsConfigWithRefs = z
|
|
112
|
+
.object({
|
|
113
|
+
references: z
|
|
114
|
+
.array(z.object({
|
|
115
|
+
path: z.string().nullable(),
|
|
116
|
+
}))
|
|
117
|
+
.default([]),
|
|
118
|
+
})
|
|
119
|
+
.passthrough();
|
|
120
|
+
try {
|
|
121
|
+
const content = fs.readFileSync(tsConfigFilePath, "utf-8");
|
|
122
|
+
const config = tsConfigWithRefs.parse(JSON.parse(content));
|
|
123
|
+
const tsConfigDir = path.dirname(tsConfigFilePath);
|
|
124
|
+
return config.references
|
|
125
|
+
.map((ref) => {
|
|
126
|
+
if (!ref.path)
|
|
127
|
+
return null;
|
|
128
|
+
const refDir = path.resolve(tsConfigDir, ref.path);
|
|
129
|
+
// Scenario: `ref.path` points directly to the tsconfig file
|
|
130
|
+
if (ref.path.endsWith(".json") && fs.existsSync(refDir)) {
|
|
131
|
+
return refDir;
|
|
132
|
+
}
|
|
133
|
+
// Otherwise, look for `tsconfig.json` in the referenced directory.
|
|
134
|
+
// We don't support other names yet. Workaround is to reference
|
|
135
|
+
// the tsconfig path or directly run the codemod with the path
|
|
136
|
+
// to the tsconfig you want to migrate.
|
|
137
|
+
const refTsConfig = path.join(refDir, "tsconfig.json");
|
|
138
|
+
if (fs.existsSync(refTsConfig)) {
|
|
139
|
+
return refTsConfig;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
})
|
|
143
|
+
.filter((p) => p !== null);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
88
149
|
function validateTsConfigPath(path) {
|
|
89
150
|
if (!path.endsWith(".json")) {
|
|
90
151
|
return {
|
package/dist/migrate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SyntaxKind } from "ts-morph";
|
|
2
2
|
import { findRootExpression } from "./ast.js";
|
|
3
|
-
import { AstroZodModuleSpecifiers, collectZodImportDeclarations, collectZodReferences, getZodName, } from "./collect-imports.js";
|
|
3
|
+
import { AstroZodModuleSpecifiers, collectDerivedZodSchemaReferences, collectZodImportDeclarations, collectZodReferences, getZodName, } from "./collect-imports.js";
|
|
4
4
|
import { convertAstroDeprecatedZodImports } from "./convert-astro-imports.js";
|
|
5
5
|
import { convertZArrayPatternsToTopLevelApi, convertZCoercePatternsToTopLevelApi, convertZFunctionPatternsToTopLevelApi, convertZNumberPatternsToZInt, convertZObjectPatternsToTopLevelApi, convertZRecordPatternsToTopLevelApi, convertZStringPatternsToTopLevelApi, } from "./convert-name-to-top-level-api.js";
|
|
6
6
|
import { convertDeprecatedErrorKeysToErrorFunction, convertErrorMapToErrorFunction, convertMessageKeyToError, convertZodErrorAddIssueToDirectPushes, convertZodErrorToTreeifyError, } from "./convert-zod-errors.js";
|
|
@@ -26,8 +26,9 @@ export function migrateZodV3ToV4(sourceFile, options = {}) {
|
|
|
26
26
|
});
|
|
27
27
|
// Collect references before modifying imports
|
|
28
28
|
const zodReferences = collectZodReferences(importDeclarations);
|
|
29
|
+
const derivedReferences = collectDerivedZodSchemaReferences(zodReferences);
|
|
29
30
|
replaceDeletedTypes(importDeclarations, zodReferences);
|
|
30
|
-
zodReferences.forEach((node) => {
|
|
31
|
+
[...zodReferences, ...derivedReferences].forEach((node) => {
|
|
31
32
|
if (node.wasForgotten()) {
|
|
32
33
|
return;
|
|
33
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-v3-to-v4",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Migrate Zod from v3 to v4",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zod",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"format-check": "prettier . --list-different",
|
|
34
34
|
"playground": "pnpm playground:interactive playground/tsconfig.json",
|
|
35
35
|
"playground:interactive": "node --experimental-strip-types --disable-warning=ExperimentalWarning src/index.ts",
|
|
36
|
+
"playground:monorepo": "pnpm playground:interactive playground-monorepo/tsconfig.json",
|
|
36
37
|
"prepare": "husky",
|
|
37
38
|
"prepublishOnly": "pnpm typecheck && pnpm test && pnpm build",
|
|
38
39
|
"test": "vitest run",
|