@gabrielbryk/json-schema-to-zod 2.8.0 → 2.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/core/analyzeSchema.js +62 -0
  3. package/dist/cjs/core/emitZod.js +141 -0
  4. package/dist/cjs/generators/generateBundle.js +103 -59
  5. package/dist/cjs/index.js +4 -0
  6. package/dist/cjs/jsonSchemaToZod.js +5 -167
  7. package/dist/cjs/parsers/parseSchema.js +124 -24
  8. package/dist/cjs/utils/buildRefRegistry.js +56 -0
  9. package/dist/cjs/utils/resolveUri.js +16 -0
  10. package/dist/esm/Types.js +1 -2
  11. package/dist/esm/cli.js +10 -12
  12. package/dist/esm/core/analyzeSchema.js +58 -0
  13. package/dist/esm/core/emitZod.js +137 -0
  14. package/dist/esm/generators/generateBundle.js +104 -64
  15. package/dist/esm/index.js +34 -46
  16. package/dist/esm/jsonSchemaToZod.js +5 -171
  17. package/dist/esm/parsers/parseAllOf.js +5 -8
  18. package/dist/esm/parsers/parseAnyOf.js +6 -10
  19. package/dist/esm/parsers/parseArray.js +11 -15
  20. package/dist/esm/parsers/parseBoolean.js +1 -5
  21. package/dist/esm/parsers/parseConst.js +1 -5
  22. package/dist/esm/parsers/parseDefault.js +3 -7
  23. package/dist/esm/parsers/parseEnum.js +1 -5
  24. package/dist/esm/parsers/parseIfThenElse.js +5 -9
  25. package/dist/esm/parsers/parseMultipleType.js +3 -7
  26. package/dist/esm/parsers/parseNot.js +4 -8
  27. package/dist/esm/parsers/parseNull.js +1 -5
  28. package/dist/esm/parsers/parseNullable.js +4 -8
  29. package/dist/esm/parsers/parseNumber.js +11 -15
  30. package/dist/esm/parsers/parseObject.js +25 -28
  31. package/dist/esm/parsers/parseOneOf.js +6 -10
  32. package/dist/esm/parsers/parseSchema.js +183 -87
  33. package/dist/esm/parsers/parseSimpleDiscriminatedOneOf.js +6 -10
  34. package/dist/esm/parsers/parseString.js +11 -15
  35. package/dist/esm/utils/anyOrUnknown.js +1 -5
  36. package/dist/esm/utils/buildRefRegistry.js +52 -0
  37. package/dist/esm/utils/cliTools.js +7 -13
  38. package/dist/esm/utils/cycles.js +3 -9
  39. package/dist/esm/utils/half.js +1 -5
  40. package/dist/esm/utils/jsdocs.js +3 -8
  41. package/dist/esm/utils/omit.js +1 -5
  42. package/dist/esm/utils/resolveUri.js +12 -0
  43. package/dist/esm/utils/withMessage.js +1 -4
  44. package/dist/esm/zodToJsonSchema.js +1 -4
  45. package/dist/types/Types.d.ts +28 -0
  46. package/dist/types/core/analyzeSchema.d.ts +24 -0
  47. package/dist/types/core/emitZod.d.ts +2 -0
  48. package/dist/types/generators/generateBundle.d.ts +5 -0
  49. package/dist/types/index.d.ts +4 -0
  50. package/dist/types/jsonSchemaToZod.d.ts +1 -1
  51. package/dist/types/parsers/parseSchema.d.ts +2 -1
  52. package/dist/types/utils/buildRefRegistry.d.ts +12 -0
  53. package/dist/types/utils/resolveUri.d.ts +1 -0
  54. package/docs/proposals/bundle-refactor.md +43 -0
  55. package/docs/proposals/ref-anchor-support.md +65 -0
  56. package/eslint.config.js +26 -0
  57. package/package.json +10 -4
  58. /package/{jest.config.js → jest.config.cjs} +0 -0
  59. /package/{postcjs.js → postcjs.cjs} +0 -0
  60. /package/{postesm.js → postesm.cjs} +0 -0
@@ -1,61 +1,31 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateSchemaBundle = void 0;
4
- const jsonSchemaToZod_js_1 = require("../jsonSchemaToZod.js");
5
- const generateSchemaBundle = (schema, options = {}) => {
1
+ import { analyzeSchema } from "../core/analyzeSchema.js";
2
+ import { emitZod } from "../core/emitZod.js";
3
+ export const generateSchemaBundle = (schema, options = {}) => {
6
4
  const module = options.module ?? "esm";
7
- const rootName = options.splitDefs?.rootName ?? options.name ?? "RootSchema";
8
- const rootTypeName = typeof options.type === "string"
9
- ? options.type
10
- : options.splitDefs?.rootTypeName ?? (typeof options.type === "boolean" && options.type ? rootName : undefined);
11
5
  if (!schema || typeof schema !== "object") {
12
6
  throw new Error("generateSchemaBundle requires an object schema");
13
7
  }
14
- const defs = schema.$defs || schema.definitions || {};
8
+ const defs = schema.$defs || {};
9
+ const definitions = schema.definitions || {};
15
10
  const defNames = Object.keys(defs);
16
- const defInfoMap = buildDefInfoMap(defNames, defs, options);
17
- const cycles = detectCycles(defInfoMap);
18
- for (const defName of cycles) {
19
- const info = defInfoMap.get(defName);
20
- if (info)
21
- info.hasCycle = true;
22
- }
11
+ const { rootName, rootTypeName, defInfoMap } = buildBundleContext(defNames, defs, options);
23
12
  const files = [];
24
- // Generate individual $def files
25
- for (const defName of defNames) {
26
- const info = defInfoMap.get(defName);
27
- const usedRefs = new Set();
28
- const defSchemaWithDefs = {
29
- ...defs[defName],
30
- $defs: defs,
31
- };
32
- const zodSchema = (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(defSchemaWithDefs, {
13
+ const targets = planBundleTargets(schema, defs, definitions, defNames, options, rootName, rootTypeName);
14
+ for (const target of targets) {
15
+ const usedRefs = target.usedRefs;
16
+ const analysis = analyzeSchema(target.schemaWithDefs, {
33
17
  ...options,
34
18
  module,
35
- name: info.schemaName,
36
- type: info.typeName,
37
- parserOverride: createRefHandler(defName, defInfoMap, usedRefs, defs, options),
19
+ name: target.schemaName,
20
+ type: target.typeName,
21
+ parserOverride: createRefHandler(target.defName, defInfoMap, usedRefs, {
22
+ ...(target.schemaWithDefs.$defs || {}),
23
+ ...(target.schemaWithDefs.definitions || {}),
24
+ }, options),
38
25
  });
26
+ const zodSchema = emitZod(analysis);
39
27
  const finalSchema = buildSchemaFile(zodSchema, usedRefs, defInfoMap, module);
40
- const fileName = options.splitDefs?.fileName?.(defName, { isRoot: false }) ?? `${defName}.schema.ts`;
41
- files.push({ fileName, contents: finalSchema });
42
- }
43
- // Generate root schema if requested
44
- if (options.splitDefs?.includeRoot ?? true) {
45
- const usedRefs = new Set();
46
- const workflowSchemaWithDefs = {
47
- ...schema,
48
- };
49
- const workflowZodSchema = (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(workflowSchemaWithDefs, {
50
- ...options,
51
- module,
52
- name: rootName,
53
- type: rootTypeName,
54
- parserOverride: createRefHandler(null, defInfoMap, usedRefs, defs, options),
55
- });
56
- const finalWorkflow = buildSchemaFile(workflowZodSchema, usedRefs, defInfoMap, module);
57
- const rootFile = options.splitDefs?.fileName?.("root", { isRoot: true }) ?? "workflow.schema.ts";
58
- files.push({ fileName: rootFile, contents: finalWorkflow });
28
+ files.push({ fileName: target.fileName, contents: finalSchema });
59
29
  }
60
30
  // Nested types extraction (optional)
61
31
  const nestedTypesEnabled = options.nestedTypes?.enable;
@@ -69,7 +39,6 @@ const generateSchemaBundle = (schema, options = {}) => {
69
39
  }
70
40
  return { files, defNames };
71
41
  };
72
- exports.generateSchemaBundle = generateSchemaBundle;
73
42
  // ---------------------------------------------------------------------------
74
43
  // Internals
75
44
  // ---------------------------------------------------------------------------
@@ -95,6 +64,20 @@ const buildDefInfoMap = (defNames, defs, options) => {
95
64
  }
96
65
  return map;
97
66
  };
67
+ const buildBundleContext = (defNames, defs, options) => {
68
+ const defInfoMap = buildDefInfoMap(defNames, defs, options);
69
+ const cycles = detectCycles(defInfoMap);
70
+ for (const defName of cycles) {
71
+ const info = defInfoMap.get(defName);
72
+ if (info)
73
+ info.hasCycle = true;
74
+ }
75
+ const rootName = options.splitDefs?.rootName ?? options.name ?? "RootSchema";
76
+ const rootTypeName = typeof options.type === "string"
77
+ ? options.type
78
+ : options.splitDefs?.rootTypeName ?? (typeof options.type === "boolean" && options.type ? rootName : undefined);
79
+ return { defInfoMap, rootName, rootTypeName };
80
+ };
98
81
  const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options) => {
99
82
  return (schema, refs) => {
100
83
  if (typeof schema["$ref"] === "string") {
@@ -102,6 +85,10 @@ const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options
102
85
  const match = refPath.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
103
86
  if (match) {
104
87
  const refName = match[1];
88
+ // Only intercept top-level def refs (no nested path like a/$defs/x)
89
+ if (refName.includes("/")) {
90
+ return undefined;
91
+ }
105
92
  const refInfo = defInfoMap.get(refName);
106
93
  if (refInfo) {
107
94
  // Track imports when referencing other defs
@@ -118,22 +105,22 @@ const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options
118
105
  });
119
106
  if (resolved)
120
107
  return resolved;
108
+ if (isCycle && options.refResolution?.lazyCrossRefs) {
109
+ return `z.lazy(() => ${refInfo.schemaName})`;
110
+ }
121
111
  return refInfo.schemaName;
122
112
  }
113
+ // If the ref points to a local/inline $def (not part of top-level defs),
114
+ // let the default parser resolve it normally.
115
+ if (allDefs && Object.prototype.hasOwnProperty.call(allDefs, refName)) {
116
+ return undefined;
117
+ }
123
118
  }
124
119
  const unknown = options.refResolution?.onUnknownRef?.({ ref: refPath, currentDef: currentDefName });
125
120
  if (unknown)
126
121
  return unknown;
127
122
  return options.useUnknown ? "z.unknown()" : "z.any()";
128
123
  }
129
- // Inline $defs within a schema
130
- if (schema["$defs"] && typeof schema["$defs"] === "object") {
131
- return (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(schema, {
132
- ...options,
133
- module: options.module ?? "esm",
134
- parserOverride: createRefHandler(currentDefName, defInfoMap, usedRefs, allDefs, options),
135
- });
136
- }
137
124
  return undefined;
138
125
  };
139
126
  };
@@ -151,6 +138,52 @@ const buildSchemaFile = (zodCode, usedRefs, defInfoMap, module) => {
151
138
  return zodCode;
152
139
  return zodCode.replace('import { z } from "zod"', `import { z } from "zod"\n${imports.join("\n")}`);
153
140
  };
141
+ const planBundleTargets = (rootSchema, defs, definitions, defNames, options, rootName, rootTypeName) => {
142
+ const targets = [];
143
+ for (const defName of defNames) {
144
+ const defSchema = defs[defName];
145
+ const defSchemaWithDefs = {
146
+ ...defSchema,
147
+ $defs: { ...defs, ...defSchema?.$defs },
148
+ definitions: {
149
+ ...defSchema.definitions,
150
+ ...definitions,
151
+ },
152
+ };
153
+ const pascalName = toPascalCase(defName);
154
+ const schemaName = options.splitDefs?.schemaName?.(defName, { isRoot: false }) ?? `${pascalName}Schema`;
155
+ const typeName = options.splitDefs?.typeName?.(defName, { isRoot: false }) ?? pascalName;
156
+ const fileName = options.splitDefs?.fileName?.(defName, { isRoot: false }) ?? `${defName}.schema.ts`;
157
+ targets.push({
158
+ defName,
159
+ schemaWithDefs: defSchemaWithDefs,
160
+ schemaName,
161
+ typeName,
162
+ fileName,
163
+ usedRefs: new Set(),
164
+ isRoot: false,
165
+ });
166
+ }
167
+ if (options.splitDefs?.includeRoot ?? true) {
168
+ const rootFile = options.splitDefs?.fileName?.("root", { isRoot: true }) ?? "workflow.schema.ts";
169
+ targets.push({
170
+ defName: null,
171
+ schemaWithDefs: {
172
+ ...rootSchema,
173
+ definitions: {
174
+ ...rootSchema.definitions,
175
+ ...definitions,
176
+ },
177
+ },
178
+ schemaName: rootName,
179
+ typeName: rootTypeName,
180
+ fileName: rootFile,
181
+ usedRefs: new Set(),
182
+ isRoot: true,
183
+ });
184
+ }
185
+ return targets;
186
+ };
154
187
  const findRefDependencies = (schema, validDefNames) => {
155
188
  const deps = new Set();
156
189
  function traverse(obj) {
@@ -239,7 +272,7 @@ const findNestedTypesInSchema = (schema, parentTypeName, defNames, currentPath =
239
272
  if (schema === null || typeof schema !== "object")
240
273
  return nestedTypes;
241
274
  const record = schema;
242
- if (record.title && typeof record.title === "string" && record.type === "object") {
275
+ if (record.title && typeof record.title === "string") {
243
276
  const title = record.title;
244
277
  if (title !== parentTypeName && !defNames.map((d) => toPascalCase(d)).includes(title)) {
245
278
  nestedTypes.push({
@@ -250,6 +283,12 @@ const findNestedTypesInSchema = (schema, parentTypeName, defNames, currentPath =
250
283
  });
251
284
  }
252
285
  }
286
+ // inline $defs
287
+ if (record.$defs && typeof record.$defs === "object") {
288
+ for (const [_defName, defSchema] of Object.entries(record.$defs)) {
289
+ nestedTypes.push(...findNestedTypesInSchema(defSchema, parentTypeName, defNames, currentPath));
290
+ }
291
+ }
253
292
  if (record.properties && typeof record.properties === "object") {
254
293
  for (const [propName, propSchema] of Object.entries(record.properties)) {
255
294
  nestedTypes.push(...findNestedTypesInSchema(propSchema, parentTypeName, defNames, [...currentPath, propName]));
@@ -261,10 +300,10 @@ const findNestedTypesInSchema = (schema, parentTypeName, defNames, currentPath =
261
300
  }
262
301
  }
263
302
  if (record.items) {
264
- nestedTypes.push(...findNestedTypesInSchema(record.items, parentTypeName, defNames, currentPath));
303
+ nestedTypes.push(...findNestedTypesInSchema(record.items, parentTypeName, defNames, [...currentPath, "items"]));
265
304
  }
266
305
  if (record.additionalProperties && typeof record.additionalProperties === "object") {
267
- nestedTypes.push(...findNestedTypesInSchema(record.additionalProperties, parentTypeName, defNames, currentPath));
306
+ nestedTypes.push(...findNestedTypesInSchema(record.additionalProperties, parentTypeName, defNames, [...currentPath, "additionalProperties"]));
268
307
  }
269
308
  return nestedTypes;
270
309
  };
@@ -285,12 +324,13 @@ const generateNestedTypesFile = (nestedTypes) => {
285
324
  }
286
325
  byParent.get(info.parentType).push(info);
287
326
  }
288
- const imports = new Set();
327
+ const imports = new Map(); // file -> type name
289
328
  for (const info of nestedTypes) {
290
- imports.add(info.file);
329
+ if (!imports.has(info.file)) {
330
+ imports.set(info.file, info.parentType);
331
+ }
291
332
  }
292
- for (const file of [...imports].sort()) {
293
- const typeName = file === "workflow" ? "Workflow" : toPascalCase(file);
333
+ for (const [file, typeName] of [...imports.entries()].sort()) {
294
334
  lines.push(`import type { ${typeName} } from './${file}.schema.js';`);
295
335
  }
296
336
  lines.push("");
package/dist/esm/index.js CHANGED
@@ -1,46 +1,34 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./Types.js"), exports);
18
- __exportStar(require("./generators/generateBundle.js"), exports);
19
- __exportStar(require("./jsonSchemaToZod.js"), exports);
20
- __exportStar(require("./parsers/parseAllOf.js"), exports);
21
- __exportStar(require("./parsers/parseAnyOf.js"), exports);
22
- __exportStar(require("./parsers/parseArray.js"), exports);
23
- __exportStar(require("./parsers/parseBoolean.js"), exports);
24
- __exportStar(require("./parsers/parseConst.js"), exports);
25
- __exportStar(require("./parsers/parseDefault.js"), exports);
26
- __exportStar(require("./parsers/parseEnum.js"), exports);
27
- __exportStar(require("./parsers/parseIfThenElse.js"), exports);
28
- __exportStar(require("./parsers/parseMultipleType.js"), exports);
29
- __exportStar(require("./parsers/parseNot.js"), exports);
30
- __exportStar(require("./parsers/parseNull.js"), exports);
31
- __exportStar(require("./parsers/parseNullable.js"), exports);
32
- __exportStar(require("./parsers/parseNumber.js"), exports);
33
- __exportStar(require("./parsers/parseObject.js"), exports);
34
- __exportStar(require("./parsers/parseOneOf.js"), exports);
35
- __exportStar(require("./parsers/parseSchema.js"), exports);
36
- __exportStar(require("./parsers/parseSimpleDiscriminatedOneOf.js"), exports);
37
- __exportStar(require("./parsers/parseString.js"), exports);
38
- __exportStar(require("./utils/anyOrUnknown.js"), exports);
39
- __exportStar(require("./utils/cycles.js"), exports);
40
- __exportStar(require("./utils/half.js"), exports);
41
- __exportStar(require("./utils/jsdocs.js"), exports);
42
- __exportStar(require("./utils/omit.js"), exports);
43
- __exportStar(require("./utils/withMessage.js"), exports);
44
- __exportStar(require("./zodToJsonSchema.js"), exports);
45
- const jsonSchemaToZod_js_1 = require("./jsonSchemaToZod.js");
46
- exports.default = jsonSchemaToZod_js_1.jsonSchemaToZod;
1
+ export * from "./Types.js";
2
+ export * from "./core/analyzeSchema.js";
3
+ export * from "./core/emitZod.js";
4
+ export * from "./generators/generateBundle.js";
5
+ export * from "./jsonSchemaToZod.js";
6
+ export * from "./parsers/parseAllOf.js";
7
+ export * from "./parsers/parseAnyOf.js";
8
+ export * from "./parsers/parseArray.js";
9
+ export * from "./parsers/parseBoolean.js";
10
+ export * from "./parsers/parseConst.js";
11
+ export * from "./parsers/parseDefault.js";
12
+ export * from "./parsers/parseEnum.js";
13
+ export * from "./parsers/parseIfThenElse.js";
14
+ export * from "./parsers/parseMultipleType.js";
15
+ export * from "./parsers/parseNot.js";
16
+ export * from "./parsers/parseNull.js";
17
+ export * from "./parsers/parseNullable.js";
18
+ export * from "./parsers/parseNumber.js";
19
+ export * from "./parsers/parseObject.js";
20
+ export * from "./parsers/parseOneOf.js";
21
+ export * from "./parsers/parseSchema.js";
22
+ export * from "./parsers/parseSimpleDiscriminatedOneOf.js";
23
+ export * from "./parsers/parseString.js";
24
+ export * from "./utils/anyOrUnknown.js";
25
+ export * from "./utils/buildRefRegistry.js";
26
+ export * from "./utils/cycles.js";
27
+ export * from "./utils/half.js";
28
+ export * from "./utils/jsdocs.js";
29
+ export * from "./utils/omit.js";
30
+ export * from "./utils/resolveUri.js";
31
+ export * from "./utils/withMessage.js";
32
+ export * from "./zodToJsonSchema.js";
33
+ import { jsonSchemaToZod } from "./jsonSchemaToZod.js";
34
+ export default jsonSchemaToZod;
@@ -1,172 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.jsonSchemaToZod = void 0;
4
- const parseSchema_js_1 = require("./parsers/parseSchema.js");
5
- const jsdocs_js_1 = require("./utils/jsdocs.js");
6
- const cycles_js_1 = require("./utils/cycles.js");
7
- const jsonSchemaToZod = (schema, { module, name, type, noImport, ...rest } = {}) => {
8
- if (type && (!name || module !== "esm")) {
9
- throw new Error("Option `type` requires `name` to be set and `module` to be `esm`");
10
- }
11
- const refNameByPointer = new Map();
12
- const usedNames = new Set();
13
- const exportRefs = rest.exportRefs ?? true;
14
- const withMeta = rest.withMeta ?? true;
15
- if (name)
16
- usedNames.add(name);
17
- // Pass 1: collect declarations/dependencies to detect cycles before emitting
18
- const pass1 = {
19
- module,
20
- name,
21
- path: [],
22
- seen: new Map(),
23
- declarations: new Map(),
24
- dependencies: new Map(),
25
- inProgress: new Set(),
26
- refNameByPointer,
27
- usedNames,
28
- root: schema,
29
- currentSchemaName: name,
30
- ...rest,
31
- withMeta,
32
- };
33
- (0, parseSchema_js_1.parseSchema)(schema, pass1);
34
- const names = Array.from(pass1.declarations.keys());
35
- const cycleRefNames = (0, cycles_js_1.detectCycles)(names, pass1.dependencies);
36
- const { componentByName } = (0, cycles_js_1.computeScc)(names, pass1.dependencies);
37
- // Pass 2: generate with cycle awareness
38
- const declarations = new Map();
39
- const dependencies = new Map();
40
- const parsedSchema = (0, parseSchema_js_1.parseSchema)(schema, {
41
- module,
42
- name,
43
- path: [],
44
- seen: new Map(),
45
- declarations,
46
- dependencies,
47
- inProgress: new Set(),
48
- refNameByPointer,
49
- usedNames,
50
- root: schema,
51
- currentSchemaName: name,
52
- cycleRefNames,
53
- cycleComponentByName: componentByName,
54
- ...rest,
55
- withMeta,
56
- });
57
- const declarationBlock = declarations.size
58
- ? orderDeclarations(Array.from(declarations.entries()), dependencies)
59
- .map(([refName, value]) => {
60
- const shouldExport = exportRefs && module === "esm";
61
- const decl = `${shouldExport ? "export " : ""}const ${refName} = ${value}`;
62
- return decl;
63
- })
64
- .join("\n")
65
- : "";
66
- const jsdocs = rest.withJsdocs && typeof schema !== "boolean" && schema.description
67
- ? (0, jsdocs_js_1.expandJsdocs)(schema.description)
68
- : "";
69
- const lines = [];
70
- if (module === "cjs" && !noImport) {
71
- lines.push(`const { z } = require("zod")`);
72
- }
73
- if (module === "esm" && !noImport) {
74
- lines.push(`import { z } from "zod"`);
75
- }
76
- if (declarationBlock) {
77
- lines.push(declarationBlock);
78
- }
79
- if (module === "cjs") {
80
- const payload = name ? `{ ${JSON.stringify(name)}: ${parsedSchema} }` : parsedSchema;
81
- lines.push(`${jsdocs}module.exports = ${payload}`);
82
- }
83
- else if (module === "esm") {
84
- lines.push(`${jsdocs}export ${name ? `const ${name} =` : `default`} ${parsedSchema}`);
85
- }
86
- else if (name) {
87
- lines.push(`${jsdocs}const ${name} = ${parsedSchema}`);
88
- }
89
- else {
90
- lines.push(`${jsdocs}${parsedSchema}`);
91
- }
92
- let typeLine;
93
- if (type && name) {
94
- let typeName = typeof type === "string"
95
- ? type
96
- : `${name[0].toUpperCase()}${name.substring(1)}`;
97
- typeLine = `export type ${typeName} = z.infer<typeof ${name}>`;
98
- }
99
- const joined = lines.filter(Boolean).join("\n\n");
100
- const combined = typeLine ? `${joined}\n${typeLine}` : joined;
101
- const shouldEndWithNewline = module === "esm" || module === "cjs";
102
- return `${combined}${shouldEndWithNewline ? "\n" : ""}`;
1
+ import { analyzeSchema } from "./core/analyzeSchema.js";
2
+ import { emitZod } from "./core/emitZod.js";
3
+ export const jsonSchemaToZod = (schema, options = {}) => {
4
+ const analysis = analyzeSchema(schema, options);
5
+ return emitZod(analysis);
103
6
  };
104
- exports.jsonSchemaToZod = jsonSchemaToZod;
105
- function orderDeclarations(entries, dependencies) {
106
- const valueByName = new Map(entries);
107
- const depGraph = new Map();
108
- // Seed with explicit dependencies recorded during parsing
109
- for (const [from, set] of dependencies.entries()) {
110
- const onlyKnown = new Set();
111
- for (const dep of set) {
112
- if (valueByName.has(dep) && dep !== from) {
113
- onlyKnown.add(dep);
114
- }
115
- }
116
- if (onlyKnown.size)
117
- depGraph.set(from, onlyKnown);
118
- }
119
- // Also infer deps by scanning declaration bodies for referenced names
120
- const names = Array.from(valueByName.keys());
121
- for (const [name, value] of entries) {
122
- const deps = depGraph.get(name) ?? new Set();
123
- for (const candidate of names) {
124
- if (candidate === name)
125
- continue;
126
- const matcher = new RegExp(`\\b${candidate}\\b`);
127
- if (matcher.test(value)) {
128
- deps.add(candidate);
129
- }
130
- }
131
- if (deps.size)
132
- depGraph.set(name, deps);
133
- }
134
- const ordered = [];
135
- const perm = new Set();
136
- const temp = new Set();
137
- const visit = (name) => {
138
- if (perm.has(name))
139
- return;
140
- if (temp.has(name)) {
141
- // Cycle detected; break it but still include the node.
142
- temp.delete(name);
143
- perm.add(name);
144
- ordered.push(name);
145
- return;
146
- }
147
- temp.add(name);
148
- const deps = depGraph.get(name);
149
- if (deps) {
150
- for (const dep of deps) {
151
- if (valueByName.has(dep)) {
152
- visit(dep);
153
- }
154
- }
155
- }
156
- temp.delete(name);
157
- perm.add(name);
158
- ordered.push(name);
159
- };
160
- for (const name of valueByName.keys()) {
161
- visit(name);
162
- }
163
- const unique = [];
164
- const seen = new Set();
165
- for (const name of ordered) {
166
- if (!seen.has(name)) {
167
- seen.add(name);
168
- unique.push(name);
169
- }
170
- }
171
- return unique.map((name) => [name, valueByName.get(name)]);
172
- }
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseAllOf = parseAllOf;
4
- const parseSchema_js_1 = require("./parseSchema.js");
5
- const half_js_1 = require("../utils/half.js");
1
+ import { parseSchema } from "./parseSchema.js";
2
+ import { half } from "../utils/half.js";
6
3
  const originalIndex = Symbol("Original index");
7
4
  const ensureOriginalIndex = (arr) => {
8
5
  let newArr = [];
@@ -20,19 +17,19 @@ const ensureOriginalIndex = (arr) => {
20
17
  }
21
18
  return newArr;
22
19
  };
23
- function parseAllOf(schema, refs) {
20
+ export function parseAllOf(schema, refs) {
24
21
  if (schema.allOf.length === 0) {
25
22
  return "z.never()";
26
23
  }
27
24
  else if (schema.allOf.length === 1) {
28
25
  const item = schema.allOf[0];
29
- return (0, parseSchema_js_1.parseSchema)(item, {
26
+ return parseSchema(item, {
30
27
  ...refs,
31
28
  path: [...refs.path, "allOf", item[originalIndex]],
32
29
  });
33
30
  }
34
31
  else {
35
- const [left, right] = (0, half_js_1.half)(ensureOriginalIndex(schema.allOf));
32
+ const [left, right] = half(ensureOriginalIndex(schema.allOf));
36
33
  return `z.intersection(${parseAllOf({ allOf: left }, refs)}, ${parseAllOf({
37
34
  allOf: right,
38
35
  }, refs)})`;
@@ -1,18 +1,14 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseAnyOf = void 0;
4
- const parseSchema_js_1 = require("./parseSchema.js");
5
- const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
6
- const parseAnyOf = (schema, refs) => {
1
+ import { parseSchema } from "./parseSchema.js";
2
+ import { anyOrUnknown } from "../utils/anyOrUnknown.js";
3
+ export const parseAnyOf = (schema, refs) => {
7
4
  return schema.anyOf.length
8
5
  ? schema.anyOf.length === 1
9
- ? (0, parseSchema_js_1.parseSchema)(schema.anyOf[0], {
6
+ ? parseSchema(schema.anyOf[0], {
10
7
  ...refs,
11
8
  path: [...refs.path, "anyOf", 0],
12
9
  })
13
10
  : `z.union([${schema.anyOf
14
- .map((schema, i) => (0, parseSchema_js_1.parseSchema)(schema, { ...refs, path: [...refs.path, "anyOf", i] }))
11
+ .map((schema, i) => parseSchema(schema, { ...refs, path: [...refs.path, "anyOf", i] }))
15
12
  .join(", ")}])`
16
- : (0, anyOrUnknown_js_1.anyOrUnknown)(refs);
13
+ : anyOrUnknown(refs);
17
14
  };
18
- exports.parseAnyOf = parseAnyOf;
@@ -1,14 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseArray = void 0;
4
- const withMessage_js_1 = require("../utils/withMessage.js");
5
- const parseSchema_js_1 = require("./parseSchema.js");
6
- const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
7
- const parseArray = (schema, refs) => {
1
+ import { withMessage } from "../utils/withMessage.js";
2
+ import { parseSchema } from "./parseSchema.js";
3
+ import { anyOrUnknown } from "../utils/anyOrUnknown.js";
4
+ export const parseArray = (schema, refs) => {
8
5
  if (Array.isArray(schema.items)) {
9
- let tuple = `z.tuple([${schema.items.map((v, i) => (0, parseSchema_js_1.parseSchema)(v, { ...refs, path: [...refs.path, "items", i] }))}])`;
6
+ let tuple = `z.tuple([${schema.items.map((v, i) => parseSchema(v, { ...refs, path: [...refs.path, "items", i] }))}])`;
10
7
  if (schema.contains) {
11
- const containsSchema = (0, parseSchema_js_1.parseSchema)(schema.contains, {
8
+ const containsSchema = parseSchema(schema.contains, {
12
9
  ...refs,
13
10
  path: [...refs.path, "contains"],
14
11
  });
@@ -27,18 +24,18 @@ const parseArray = (schema, refs) => {
27
24
  return tuple;
28
25
  }
29
26
  let r = !schema.items
30
- ? `z.array(${(0, anyOrUnknown_js_1.anyOrUnknown)(refs)})`
31
- : `z.array(${(0, parseSchema_js_1.parseSchema)(schema.items, {
27
+ ? `z.array(${anyOrUnknown(refs)})`
28
+ : `z.array(${parseSchema(schema.items, {
32
29
  ...refs,
33
30
  path: [...refs.path, "items"],
34
31
  })})`;
35
- r += (0, withMessage_js_1.withMessage)(schema, "minItems", ({ json }) => ({
32
+ r += withMessage(schema, "minItems", ({ json }) => ({
36
33
  opener: `.min(${json}`,
37
34
  closer: ")",
38
35
  messagePrefix: ", { error: ",
39
36
  messageCloser: " })",
40
37
  }));
41
- r += (0, withMessage_js_1.withMessage)(schema, "maxItems", ({ json }) => ({
38
+ r += withMessage(schema, "maxItems", ({ json }) => ({
42
39
  opener: `.max(${json}`,
43
40
  closer: ")",
44
41
  messagePrefix: ", { error: ",
@@ -69,7 +66,7 @@ const parseArray = (schema, refs) => {
69
66
  })`;
70
67
  }
71
68
  if (schema.contains) {
72
- const containsSchema = (0, parseSchema_js_1.parseSchema)(schema.contains, {
69
+ const containsSchema = parseSchema(schema.contains, {
73
70
  ...refs,
74
71
  path: [...refs.path, "contains"],
75
72
  });
@@ -87,4 +84,3 @@ const parseArray = (schema, refs) => {
87
84
  }
88
85
  return r;
89
86
  };
90
- exports.parseArray = parseArray;
@@ -1,7 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseBoolean = void 0;
4
- const parseBoolean = (_schema) => {
1
+ export const parseBoolean = (_schema) => {
5
2
  return "z.boolean()";
6
3
  };
7
- exports.parseBoolean = parseBoolean;
@@ -1,7 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseConst = void 0;
4
- const parseConst = (schema) => {
1
+ export const parseConst = (schema) => {
5
2
  return `z.literal(${JSON.stringify(schema.const)})`;
6
3
  };
7
- exports.parseConst = parseConst;