@kubb/plugin-zod 5.0.0-beta.79 → 5.0.0-beta.81
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/dist/index.cjs +136 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -36
- package/dist/index.js +136 -85
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -55,7 +55,8 @@ type OperationParamsResolver = {
|
|
|
55
55
|
*
|
|
56
56
|
* Each key is a `SchemaType` string (e.g. `'date'`, `'string'`). The function
|
|
57
57
|
* replaces the built-in handler for that node type. Use `this.transform` to
|
|
58
|
-
* recurse into nested schema nodes,
|
|
58
|
+
* recurse into nested schema nodes, `this.base` to reuse the output of the
|
|
59
|
+
* handler being replaced, and `this.options` to read printer options.
|
|
59
60
|
*
|
|
60
61
|
* @example Override the `date` handler
|
|
61
62
|
* ```ts
|
|
@@ -63,7 +64,20 @@ type OperationParamsResolver = {
|
|
|
63
64
|
* printer: {
|
|
64
65
|
* nodes: {
|
|
65
66
|
* date(node) {
|
|
66
|
-
* return 'z.
|
|
67
|
+
* return 'z.iso.date()'
|
|
68
|
+
* },
|
|
69
|
+
* },
|
|
70
|
+
* },
|
|
71
|
+
* })
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example Wrap the built-in output
|
|
75
|
+
* ```ts
|
|
76
|
+
* pluginZod({
|
|
77
|
+
* printer: {
|
|
78
|
+
* nodes: {
|
|
79
|
+
* object(node) {
|
|
80
|
+
* return `${this.base(node)}.openapi(${JSON.stringify({ description: node.description })})`
|
|
67
81
|
* },
|
|
68
82
|
* },
|
|
69
83
|
* },
|
|
@@ -93,10 +107,6 @@ type PrinterZodOptions = {
|
|
|
93
107
|
* Date format in the OpenAPI spec (`'date'` or `'date-time'`).
|
|
94
108
|
*/
|
|
95
109
|
dateType?: AdapterOas['resolvedOptions']['dateType'];
|
|
96
|
-
/**
|
|
97
|
-
* Hook to transform generated Zod schema before output.
|
|
98
|
-
*/
|
|
99
|
-
wrapOutput?: PluginZod['resolvedOptions']['wrapOutput'];
|
|
100
110
|
/**
|
|
101
111
|
* Transforms raw schema names into valid JavaScript identifiers.
|
|
102
112
|
*/
|
|
@@ -112,8 +122,8 @@ type PrinterZodOptions = {
|
|
|
112
122
|
cyclicSchemas?: ReadonlySet<string>;
|
|
113
123
|
/**
|
|
114
124
|
* Print direction for `dateType: 'date'` fields (`Date` in TypeScript):
|
|
115
|
-
* - `'output'` (default)
|
|
116
|
-
* - `'input'
|
|
125
|
+
* - `'output'` (default): decode the wire `string` into a `Date` (response bodies).
|
|
126
|
+
* - `'input'`: encode a `Date` back into the wire `string` (request bodies/params).
|
|
117
127
|
*
|
|
118
128
|
* Diverging the directions requires the generator to emit an `${name}InputSchema`
|
|
119
129
|
* variant for each date-bearing component.
|
|
@@ -154,7 +164,8 @@ declare const printerZod: (options?: PrinterZodOptions | undefined) => ast.Print
|
|
|
154
164
|
*
|
|
155
165
|
* Each key is a `SchemaType` string (e.g. `'date'`, `'string'`). The function
|
|
156
166
|
* replaces the built-in handler for that node type. Use `this.transform` to
|
|
157
|
-
* recurse into nested schema nodes,
|
|
167
|
+
* recurse into nested schema nodes, `this.base` to reuse the output of the
|
|
168
|
+
* handler being replaced, and `this.options` to read printer options.
|
|
158
169
|
*
|
|
159
170
|
* @example Override the `date` handler
|
|
160
171
|
* ```ts
|
|
@@ -185,10 +196,6 @@ type PrinterZodMiniOptions = {
|
|
|
185
196
|
* @default 'literal'
|
|
186
197
|
*/
|
|
187
198
|
regexType?: PluginZod['resolvedOptions']['regexType'];
|
|
188
|
-
/**
|
|
189
|
-
* Hook to transform generated Zod schema before output.
|
|
190
|
-
*/
|
|
191
|
-
wrapOutput?: PluginZod['resolvedOptions']['wrapOutput'];
|
|
192
199
|
/**
|
|
193
200
|
* Transforms raw schema names into valid JavaScript identifiers.
|
|
194
201
|
*/
|
|
@@ -218,7 +225,7 @@ type PrinterZodMiniOptions = {
|
|
|
218
225
|
*/
|
|
219
226
|
type PrinterZodMiniFactory = ast.PrinterFactoryOptions<'zod-mini', PrinterZodMiniOptions, string, string>;
|
|
220
227
|
/**
|
|
221
|
-
* Zod v4
|
|
228
|
+
* Zod v4 Mini printer built with `definePrinter`.
|
|
222
229
|
*
|
|
223
230
|
* Converts a `SchemaNode` AST into a Zod v4 code string using the functional API
|
|
224
231
|
* (`z.optional(z.string())`) for improved tree-shaking. See {@link printerZod} for the chainable API.
|
|
@@ -350,12 +357,6 @@ type Options = OutputOptions & {
|
|
|
350
357
|
* @default mini ? 'zod/mini' : 'zod'
|
|
351
358
|
*/
|
|
352
359
|
importPath?: 'zod' | 'zod/mini' | (string & {});
|
|
353
|
-
/**
|
|
354
|
-
* Tie each Zod schema to its TypeScript type from `@kubb/plugin-ts`. Requires
|
|
355
|
-
* `@kubb/plugin-ts` in the plugins list. TypeScript fails compilation when the
|
|
356
|
-
* schema drifts from the type.
|
|
357
|
-
*/
|
|
358
|
-
typed?: boolean;
|
|
359
360
|
/**
|
|
360
361
|
* Export a `z.infer<typeof schema>` type alias next to every generated schema.
|
|
361
362
|
* Lets the Zod schema act as the single source of truth.
|
|
@@ -367,6 +368,10 @@ type Options = OutputOptions & {
|
|
|
367
368
|
* - `true` coerces strings, numbers, and dates.
|
|
368
369
|
* - Object form picks per-primitive coercion.
|
|
369
370
|
*
|
|
371
|
+
* `dates` applies to fields typed as `Date` (adapter `dateType: 'date'`): they
|
|
372
|
+
* validate with `z.coerce.date()` instead of the string-to-Date codec. Fields
|
|
373
|
+
* kept as ISO strings (`z.iso.date()`, `z.iso.datetime()`) are never coerced.
|
|
374
|
+
*
|
|
370
375
|
* @default false
|
|
371
376
|
* @see https://zod.dev/?id=coercion-for-primitives
|
|
372
377
|
*/
|
|
@@ -377,16 +382,16 @@ type Options = OutputOptions & {
|
|
|
377
382
|
};
|
|
378
383
|
/**
|
|
379
384
|
* Validator for `format: uuid` properties.
|
|
380
|
-
* - `'uuid'
|
|
381
|
-
* - `'guid'
|
|
385
|
+
* - `'uuid'`: `z.uuid()`. Standard RFC 4122.
|
|
386
|
+
* - `'guid'`: `z.guid()`. Accepts Microsoft-style GUIDs.
|
|
382
387
|
*
|
|
383
388
|
* @default 'uuid'
|
|
384
389
|
*/
|
|
385
390
|
guidType?: 'uuid' | 'guid';
|
|
386
391
|
/**
|
|
387
392
|
* Output form for an OpenAPI `pattern` inside `.regex(...)`.
|
|
388
|
-
* - `'literal'
|
|
389
|
-
* - `'constructor'
|
|
393
|
+
* - `'literal'`: a regex literal, `.regex(/^[a-z]+$/)`.
|
|
394
|
+
* - `'constructor'`: the `RegExp` constructor, `.regex(new RegExp("^[a-z]+$"))`.
|
|
390
395
|
*
|
|
391
396
|
* @default 'literal'
|
|
392
397
|
*/
|
|
@@ -399,15 +404,6 @@ type Options = OutputOptions & {
|
|
|
399
404
|
* @beta
|
|
400
405
|
*/
|
|
401
406
|
mini?: boolean;
|
|
402
|
-
/**
|
|
403
|
-
* Wrap the generated Zod schema string with extra calls. Receives the raw output
|
|
404
|
-
* and the originating `SchemaNode`. Useful for round-tripping OpenAPI metadata
|
|
405
|
-
* back into Zod (e.g. `.openapi(...)`).
|
|
406
|
-
*/
|
|
407
|
-
wrapOutput?: (arg: {
|
|
408
|
-
output: string;
|
|
409
|
-
schema: ast.SchemaNode;
|
|
410
|
-
}) => string | undefined;
|
|
411
407
|
/**
|
|
412
408
|
* Override how schema and operation names are built. Methods you omit fall back
|
|
413
409
|
* to the default `resolverZod`.
|
|
@@ -431,14 +427,12 @@ type ResolvedOptions = {
|
|
|
431
427
|
include: Array<Include> | undefined;
|
|
432
428
|
override: Array<Override<ResolvedOptions>>;
|
|
433
429
|
group: Group | null;
|
|
434
|
-
typed: NonNullable<Options['typed']>;
|
|
435
430
|
inferred: NonNullable<Options['inferred']>;
|
|
436
431
|
importPath: NonNullable<Options['importPath']>;
|
|
437
432
|
coercion: NonNullable<Options['coercion']>;
|
|
438
433
|
guidType: NonNullable<Options['guidType']>;
|
|
439
434
|
regexType: NonNullable<Options['regexType']>;
|
|
440
435
|
mini: NonNullable<Options['mini']>;
|
|
441
|
-
wrapOutput: Options['wrapOutput'];
|
|
442
436
|
printer: Options['printer'];
|
|
443
437
|
};
|
|
444
438
|
type PluginZod = PluginFactoryOptions<'plugin-zod', Options, ResolvedOptions, ResolverZod>;
|
|
@@ -485,7 +479,7 @@ declare const pluginZodName = "plugin-zod";
|
|
|
485
479
|
* pluginTs(),
|
|
486
480
|
* pluginZod({
|
|
487
481
|
* output: { path: './zod' },
|
|
488
|
-
*
|
|
482
|
+
* inferred: true,
|
|
489
483
|
* }),
|
|
490
484
|
* ],
|
|
491
485
|
* })
|
package/dist/index.js
CHANGED
|
@@ -410,6 +410,46 @@ function collectCodecRefNames(node) {
|
|
|
410
410
|
return ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 });
|
|
411
411
|
}
|
|
412
412
|
/**
|
|
413
|
+
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
414
|
+
* argument. A catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
415
|
+
* on `.and(…)`.
|
|
416
|
+
*/
|
|
417
|
+
function isPlainInlineObject(node) {
|
|
418
|
+
return node.type === "object" && !node.nullable && !node.optional && !node.nullish && node.additionalProperties === void 0 && !node.patternProperties;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Whether a node renders as a bare Zod object — one that accepts `.extend(…)` and can be a
|
|
422
|
+
* `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
|
|
423
|
+
* unresolved) objects, single-member unions of an object, and object-composable `allOf`.
|
|
424
|
+
*
|
|
425
|
+
* `cyclicSchemas` bounds the recursion: a ref into a cycle renders as `z.lazy(…)`, not an object.
|
|
426
|
+
*/
|
|
427
|
+
function isObjectSchemaNode(node, cyclicSchemas) {
|
|
428
|
+
if (node.nullable || node.optional || node.nullish) return false;
|
|
429
|
+
if (node.type === "object") return true;
|
|
430
|
+
if (node.type === "ref") {
|
|
431
|
+
const refName = (node.ref ? extractRefName(node.ref) : void 0) ?? node.name;
|
|
432
|
+
if (refName && cyclicSchemas?.has(refName)) return false;
|
|
433
|
+
const resolved = syncSchemaRef(node);
|
|
434
|
+
return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
|
|
435
|
+
}
|
|
436
|
+
if (node.type === "union") {
|
|
437
|
+
const members = node.members ?? [];
|
|
438
|
+
return members.length === 1 && isObjectSchemaNode(members[0], cyclicSchemas);
|
|
439
|
+
}
|
|
440
|
+
return isObjectComposableIntersection(node, cyclicSchemas);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Whether an `allOf` is a pure object composition — an object base plus inline object members whose
|
|
444
|
+
* shapes merge with `.extend(…)`. These stay a Zod object instead of a `ZodIntersection`, which
|
|
445
|
+
* `z.discriminatedUnion` rejects.
|
|
446
|
+
*/
|
|
447
|
+
function isObjectComposableIntersection(node, cyclicSchemas) {
|
|
448
|
+
if (node.type !== "intersection") return false;
|
|
449
|
+
const [first, ...rest] = node.members ?? [];
|
|
450
|
+
return !!first && isObjectSchemaNode(first, cyclicSchemas) && rest.every(isPlainInlineObject);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
413
453
|
* Format a default value as a code-level literal.
|
|
414
454
|
* Objects become `{}`, primitives become their string representation, strings are quoted.
|
|
415
455
|
*/
|
|
@@ -617,6 +657,41 @@ function getMemberConstraint({ member, regexType }) {
|
|
|
617
657
|
}) || void 0;
|
|
618
658
|
}
|
|
619
659
|
/**
|
|
660
|
+
* Builds the `{ key: value, … }` shape for an object node, shared by the `z.object(...)` and
|
|
661
|
+
* `.extend(...)` renderings so they stay in lockstep.
|
|
662
|
+
*/
|
|
663
|
+
function buildZodObjectShape(ctx, node) {
|
|
664
|
+
const objectNode = ast.narrowSchema(node, "object");
|
|
665
|
+
if (!objectNode) return "{}";
|
|
666
|
+
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
667
|
+
return buildObject(mapSchemaProperties(objectNode, (schema) => {
|
|
668
|
+
const hasSelfRef = isCyclic(schema);
|
|
669
|
+
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
670
|
+
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
671
|
+
const baseOutput = ctx.transform(schema) ?? ctx.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
672
|
+
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
673
|
+
return baseOutput;
|
|
674
|
+
}).map(({ name: propName, property, output: baseOutput }) => {
|
|
675
|
+
const { schema } = property;
|
|
676
|
+
const meta = syncSchemaRef(schema);
|
|
677
|
+
const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
|
|
678
|
+
const value = applyModifiers({
|
|
679
|
+
value: baseOutput,
|
|
680
|
+
schema,
|
|
681
|
+
nullable: meta.nullable,
|
|
682
|
+
optional: schema.optional || property.required === false,
|
|
683
|
+
nullish: schema.nullish,
|
|
684
|
+
defaultValue: meta.default,
|
|
685
|
+
description: descriptionToApply,
|
|
686
|
+
examples: meta.examples
|
|
687
|
+
});
|
|
688
|
+
return isCyclic(schema) ? lazyGetter({
|
|
689
|
+
name: propName,
|
|
690
|
+
body: value
|
|
691
|
+
}) : `${objectKey(propName)}: ${value}`;
|
|
692
|
+
}));
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
620
695
|
* Zod v4 printer built with `definePrinter`.
|
|
621
696
|
*
|
|
622
697
|
* Converts a `SchemaNode` AST into a Zod v4 code string using the chainable API
|
|
@@ -657,7 +732,10 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
657
732
|
},
|
|
658
733
|
date(node) {
|
|
659
734
|
const codec = getCodec(node);
|
|
660
|
-
if (codec)
|
|
735
|
+
if (codec) {
|
|
736
|
+
if (this.options.direction === "input") return codec.encode(node);
|
|
737
|
+
return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : codec.decode(node);
|
|
738
|
+
}
|
|
661
739
|
return "z.iso.date()";
|
|
662
740
|
},
|
|
663
741
|
datetime(node) {
|
|
@@ -710,38 +788,8 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
710
788
|
return resolvedName;
|
|
711
789
|
},
|
|
712
790
|
object(node) {
|
|
713
|
-
const
|
|
714
|
-
const
|
|
715
|
-
const hasSelfRef = isCyclic(schema);
|
|
716
|
-
const savedCyclicSchemas = this.options.cyclicSchemas;
|
|
717
|
-
if (hasSelfRef) this.options.cyclicSchemas = void 0;
|
|
718
|
-
const baseOutput = this.transform(schema) ?? this.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
719
|
-
if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
|
|
720
|
-
return baseOutput;
|
|
721
|
-
}).map(({ name: propName, property, output: baseOutput }) => {
|
|
722
|
-
const { schema } = property;
|
|
723
|
-
const meta = syncSchemaRef(schema);
|
|
724
|
-
const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
|
|
725
|
-
output: baseOutput,
|
|
726
|
-
schema
|
|
727
|
-
}) || baseOutput : baseOutput;
|
|
728
|
-
const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
|
|
729
|
-
const value = applyModifiers({
|
|
730
|
-
value: wrappedOutput,
|
|
731
|
-
schema,
|
|
732
|
-
nullable: meta.nullable,
|
|
733
|
-
optional: schema.optional || property.required === false,
|
|
734
|
-
nullish: schema.nullish,
|
|
735
|
-
defaultValue: meta.default,
|
|
736
|
-
description: descriptionToApply,
|
|
737
|
-
examples: meta.examples
|
|
738
|
-
});
|
|
739
|
-
return isCyclic(schema) ? lazyGetter({
|
|
740
|
-
name: propName,
|
|
741
|
-
body: value
|
|
742
|
-
}) : `${objectKey(propName)}: ${value}`;
|
|
743
|
-
});
|
|
744
|
-
const objectBase = `z.object(${buildObject(entries)})`;
|
|
791
|
+
const entries = node.properties ?? [];
|
|
792
|
+
const objectBase = `z.object(${buildZodObjectShape(this, node)})`;
|
|
745
793
|
return (() => {
|
|
746
794
|
const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
|
|
747
795
|
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
@@ -781,7 +829,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
781
829
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema, cyclicSchemaNames) : output).filter(Boolean);
|
|
782
830
|
if (members.length === 0) return "";
|
|
783
831
|
if (members.length === 1) return members[0];
|
|
784
|
-
const allDiscriminable = nodeMembers.every((m) => (m
|
|
832
|
+
const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
785
833
|
if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
786
834
|
return `z.union(${buildList(members)})`;
|
|
787
835
|
},
|
|
@@ -792,6 +840,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
792
840
|
if (!first) return "";
|
|
793
841
|
const firstBase = this.transform(first);
|
|
794
842
|
if (!firstBase) return "";
|
|
843
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return rest.reduce((acc, member) => `${acc}.extend(${buildZodObjectShape(this, member)})`, firstBase);
|
|
795
844
|
return rest.reduce((acc, member) => {
|
|
796
845
|
const constraint = getMemberConstraint({
|
|
797
846
|
member,
|
|
@@ -801,9 +850,9 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
801
850
|
const transformed = this.transform(member);
|
|
802
851
|
return transformed ? `${acc}.and(${transformed})` : acc;
|
|
803
852
|
}, firstBase);
|
|
804
|
-
}
|
|
805
|
-
...options.nodes
|
|
853
|
+
}
|
|
806
854
|
},
|
|
855
|
+
overrides: options.nodes,
|
|
807
856
|
print(node) {
|
|
808
857
|
const { keysToOmit } = this.options;
|
|
809
858
|
const transformed = this.transform(node);
|
|
@@ -847,7 +896,39 @@ function getMemberConstraintMini({ member, regexType }) {
|
|
|
847
896
|
}) || void 0;
|
|
848
897
|
}
|
|
849
898
|
/**
|
|
850
|
-
*
|
|
899
|
+
* Builds the `{ key: value, … }` shape for an object node with the functional `zod/mini` modifiers,
|
|
900
|
+
* shared by the `z.object(...)` and `z.extend(...)` renderings so they stay in lockstep.
|
|
901
|
+
*/
|
|
902
|
+
function buildZodMiniObjectShape(ctx, node) {
|
|
903
|
+
const objectNode = ast.narrowSchema(node, "object");
|
|
904
|
+
if (!objectNode) return "{}";
|
|
905
|
+
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
906
|
+
return buildObject(mapSchemaProperties(objectNode, (schema) => {
|
|
907
|
+
const hasSelfRef = isCyclic(schema);
|
|
908
|
+
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
909
|
+
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
910
|
+
const baseOutput = ctx.transform(schema) ?? ctx.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
911
|
+
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
912
|
+
return baseOutput;
|
|
913
|
+
}).map(({ name: propName, property, output: baseOutput }) => {
|
|
914
|
+
const { schema } = property;
|
|
915
|
+
const meta = syncSchemaRef(schema);
|
|
916
|
+
const value = applyMiniModifiers({
|
|
917
|
+
value: baseOutput,
|
|
918
|
+
schema,
|
|
919
|
+
nullable: meta.nullable,
|
|
920
|
+
optional: schema.optional || property.required === false,
|
|
921
|
+
nullish: schema.nullish,
|
|
922
|
+
defaultValue: meta.default
|
|
923
|
+
});
|
|
924
|
+
return isCyclic(schema) ? lazyGetter({
|
|
925
|
+
name: propName,
|
|
926
|
+
body: value
|
|
927
|
+
}) : `${objectKey(propName)}: ${value}`;
|
|
928
|
+
}));
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Zod v4 Mini printer built with `definePrinter`.
|
|
851
932
|
*
|
|
852
933
|
* Converts a `SchemaNode` AST into a Zod v4 code string using the functional API
|
|
853
934
|
* (`z.optional(z.string())`) for improved tree-shaking. See {@link printerZod} for the chainable API.
|
|
@@ -859,6 +940,7 @@ function getMemberConstraintMini({ member, regexType }) {
|
|
|
859
940
|
* ```
|
|
860
941
|
*/
|
|
861
942
|
const printerZodMini = ast.createPrinter((options) => {
|
|
943
|
+
const cyclicSchemaNames = options.cyclicSchemas;
|
|
862
944
|
return {
|
|
863
945
|
name: "zod-mini",
|
|
864
946
|
options,
|
|
@@ -933,34 +1015,8 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
933
1015
|
return resolvedName;
|
|
934
1016
|
},
|
|
935
1017
|
object(node) {
|
|
936
|
-
const
|
|
937
|
-
const
|
|
938
|
-
const hasSelfRef = isCyclic(schema);
|
|
939
|
-
const savedCyclicSchemas = this.options.cyclicSchemas;
|
|
940
|
-
if (hasSelfRef) this.options.cyclicSchemas = void 0;
|
|
941
|
-
const baseOutput = this.transform(schema) ?? this.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
942
|
-
if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
|
|
943
|
-
return baseOutput;
|
|
944
|
-
}).map(({ name: propName, property, output: baseOutput }) => {
|
|
945
|
-
const { schema } = property;
|
|
946
|
-
const meta = syncSchemaRef(schema);
|
|
947
|
-
const value = applyMiniModifiers({
|
|
948
|
-
value: this.options.wrapOutput ? this.options.wrapOutput({
|
|
949
|
-
output: baseOutput,
|
|
950
|
-
schema
|
|
951
|
-
}) || baseOutput : baseOutput,
|
|
952
|
-
schema,
|
|
953
|
-
nullable: meta.nullable,
|
|
954
|
-
optional: schema.optional || property.required === false,
|
|
955
|
-
nullish: schema.nullish,
|
|
956
|
-
defaultValue: meta.default
|
|
957
|
-
});
|
|
958
|
-
return isCyclic(schema) ? lazyGetter({
|
|
959
|
-
name: propName,
|
|
960
|
-
body: value
|
|
961
|
-
}) : `${objectKey(propName)}: ${value}`;
|
|
962
|
-
});
|
|
963
|
-
const objectBase = `z.object(${buildObject(entries)})`;
|
|
1018
|
+
const entries = node.properties ?? [];
|
|
1019
|
+
const objectBase = `z.object(${buildZodMiniObjectShape(this, node)})`;
|
|
964
1020
|
const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
|
|
965
1021
|
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
966
1022
|
const catchallType = this.transform(node.additionalProperties);
|
|
@@ -998,7 +1054,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
998
1054
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
|
|
999
1055
|
if (members.length === 0) return "";
|
|
1000
1056
|
if (members.length === 1) return members[0];
|
|
1001
|
-
const allDiscriminable = nodeMembers.every((m) => (m
|
|
1057
|
+
const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1002
1058
|
if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
1003
1059
|
return `z.union(${buildList(members)})`;
|
|
1004
1060
|
},
|
|
@@ -1009,6 +1065,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1009
1065
|
if (!first) return "";
|
|
1010
1066
|
const firstBase = this.transform(first);
|
|
1011
1067
|
if (!firstBase) return "";
|
|
1068
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return rest.reduce((acc, member) => `z.extend(${acc}, ${buildZodMiniObjectShape(this, member)})`, firstBase);
|
|
1012
1069
|
return rest.reduce((acc, member) => {
|
|
1013
1070
|
const constraint = getMemberConstraintMini({
|
|
1014
1071
|
member,
|
|
@@ -1018,9 +1075,9 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1018
1075
|
const transformed = this.transform(member);
|
|
1019
1076
|
return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
|
|
1020
1077
|
}, firstBase);
|
|
1021
|
-
}
|
|
1022
|
-
...options.nodes
|
|
1078
|
+
}
|
|
1023
1079
|
},
|
|
1080
|
+
overrides: options.nodes,
|
|
1024
1081
|
print(node) {
|
|
1025
1082
|
const { keysToOmit } = this.options;
|
|
1026
1083
|
const transformed = this.transform(node);
|
|
@@ -1050,12 +1107,12 @@ const zodPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
|
1050
1107
|
const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
1051
1108
|
/**
|
|
1052
1109
|
* Returns the cached `output`/`input` direction printers for a resolver, building them on
|
|
1053
|
-
* first use. The `input` printer encodes `Date → string` for request bodies
|
|
1110
|
+
* first use. The `input` printer encodes `Date → string` for request bodies, and `output` decodes
|
|
1054
1111
|
* `string → Date` for responses. Schemas without `dateType: 'date'` fields print identically.
|
|
1055
1112
|
*/
|
|
1056
1113
|
function getStdPrinters(resolver, params) {
|
|
1057
1114
|
const cached = zodPrinterCache.get(resolver);
|
|
1058
|
-
if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.dateType === params.dateType) return {
|
|
1115
|
+
if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.dateType === params.dateType && cached.nodes === params.nodes) return {
|
|
1059
1116
|
output: cached.output,
|
|
1060
1117
|
input: cached.input
|
|
1061
1118
|
};
|
|
@@ -1077,7 +1134,8 @@ function getStdPrinters(resolver, params) {
|
|
|
1077
1134
|
coercion: params.coercion,
|
|
1078
1135
|
guidType: params.guidType,
|
|
1079
1136
|
regexType: params.regexType,
|
|
1080
|
-
dateType: params.dateType
|
|
1137
|
+
dateType: params.dateType,
|
|
1138
|
+
nodes: params.nodes
|
|
1081
1139
|
});
|
|
1082
1140
|
return {
|
|
1083
1141
|
output,
|
|
@@ -1086,7 +1144,7 @@ function getStdPrinters(resolver, params) {
|
|
|
1086
1144
|
}
|
|
1087
1145
|
function getMiniPrinter(resolver, params) {
|
|
1088
1146
|
const cached = zodMiniPrinterCache.get(resolver);
|
|
1089
|
-
if (cached && cached.guidType === params.guidType && cached.regexType === params.regexType) return cached.printer;
|
|
1147
|
+
if (cached && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.nodes === params.nodes) return cached.printer;
|
|
1090
1148
|
const p = printerZodMini({
|
|
1091
1149
|
...params,
|
|
1092
1150
|
resolver
|
|
@@ -1094,7 +1152,8 @@ function getMiniPrinter(resolver, params) {
|
|
|
1094
1152
|
zodMiniPrinterCache.set(resolver, {
|
|
1095
1153
|
printer: p,
|
|
1096
1154
|
guidType: params.guidType,
|
|
1097
|
-
regexType: params.regexType
|
|
1155
|
+
regexType: params.regexType,
|
|
1156
|
+
nodes: params.nodes
|
|
1098
1157
|
});
|
|
1099
1158
|
return p;
|
|
1100
1159
|
}
|
|
@@ -1109,7 +1168,7 @@ const zodGenerator = defineGenerator({
|
|
|
1109
1168
|
renderer: jsxRenderer,
|
|
1110
1169
|
schema(node, ctx) {
|
|
1111
1170
|
const { adapter, config, resolver, root } = ctx;
|
|
1112
|
-
const { output, coercion, guidType, regexType, mini,
|
|
1171
|
+
const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
|
|
1113
1172
|
const dateType = adapter.options.dateType;
|
|
1114
1173
|
if (!node.name) return;
|
|
1115
1174
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
@@ -1163,7 +1222,6 @@ const zodGenerator = defineGenerator({
|
|
|
1163
1222
|
guidType,
|
|
1164
1223
|
regexType,
|
|
1165
1224
|
dateType,
|
|
1166
|
-
wrapOutput,
|
|
1167
1225
|
cyclicSchemas,
|
|
1168
1226
|
nameMapping,
|
|
1169
1227
|
nodes: printer?.nodes
|
|
@@ -1171,7 +1229,6 @@ const zodGenerator = defineGenerator({
|
|
|
1171
1229
|
const schemaPrinter = mini ? getMiniPrinter(resolver, {
|
|
1172
1230
|
guidType,
|
|
1173
1231
|
regexType,
|
|
1174
|
-
wrapOutput,
|
|
1175
1232
|
cyclicSchemas,
|
|
1176
1233
|
nameMapping,
|
|
1177
1234
|
nodes: printer?.nodes
|
|
@@ -1231,7 +1288,7 @@ const zodGenerator = defineGenerator({
|
|
|
1231
1288
|
operation(node, ctx) {
|
|
1232
1289
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
1233
1290
|
const { adapter, config, resolver, root } = ctx;
|
|
1234
|
-
const { output, coercion, guidType, regexType, mini,
|
|
1291
|
+
const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
|
|
1235
1292
|
const dateType = adapter.options.dateType;
|
|
1236
1293
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
1237
1294
|
const params = caseParams(node.parameters, "camelcase");
|
|
@@ -1265,7 +1322,6 @@ const zodGenerator = defineGenerator({
|
|
|
1265
1322
|
const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
|
|
1266
1323
|
guidType,
|
|
1267
1324
|
regexType,
|
|
1268
|
-
wrapOutput,
|
|
1269
1325
|
resolver,
|
|
1270
1326
|
keysToOmit,
|
|
1271
1327
|
cyclicSchemas,
|
|
@@ -1274,7 +1330,6 @@ const zodGenerator = defineGenerator({
|
|
|
1274
1330
|
}) : getMiniPrinter(resolver, {
|
|
1275
1331
|
guidType,
|
|
1276
1332
|
regexType,
|
|
1277
|
-
wrapOutput,
|
|
1278
1333
|
cyclicSchemas,
|
|
1279
1334
|
nameMapping,
|
|
1280
1335
|
nodes: printer?.nodes
|
|
@@ -1283,7 +1338,6 @@ const zodGenerator = defineGenerator({
|
|
|
1283
1338
|
guidType,
|
|
1284
1339
|
regexType,
|
|
1285
1340
|
dateType,
|
|
1286
|
-
wrapOutput,
|
|
1287
1341
|
resolver,
|
|
1288
1342
|
keysToOmit,
|
|
1289
1343
|
cyclicSchemas,
|
|
@@ -1295,7 +1349,6 @@ const zodGenerator = defineGenerator({
|
|
|
1295
1349
|
guidType,
|
|
1296
1350
|
regexType,
|
|
1297
1351
|
dateType,
|
|
1298
|
-
wrapOutput,
|
|
1299
1352
|
cyclicSchemas,
|
|
1300
1353
|
nameMapping,
|
|
1301
1354
|
nodes: printer?.nodes
|
|
@@ -1549,7 +1602,7 @@ const pluginZodName = "plugin-zod";
|
|
|
1549
1602
|
* pluginTs(),
|
|
1550
1603
|
* pluginZod({
|
|
1551
1604
|
* output: { path: './zod' },
|
|
1552
|
-
*
|
|
1605
|
+
* inferred: true,
|
|
1553
1606
|
* }),
|
|
1554
1607
|
* ],
|
|
1555
1608
|
* })
|
|
@@ -1559,7 +1612,7 @@ const pluginZod = definePlugin((options) => {
|
|
|
1559
1612
|
const { output = {
|
|
1560
1613
|
path: "zod",
|
|
1561
1614
|
barrel: { type: "named" }
|
|
1562
|
-
}, group, exclude = [], include, override = [],
|
|
1615
|
+
}, group, exclude = [], include, override = [], mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, printer, resolver: userResolver, macros: userMacros } = options;
|
|
1563
1616
|
const groupConfig = createGroupConfig(group);
|
|
1564
1617
|
return {
|
|
1565
1618
|
name: pluginZodName,
|
|
@@ -1571,14 +1624,12 @@ const pluginZod = definePlugin((options) => {
|
|
|
1571
1624
|
include,
|
|
1572
1625
|
override,
|
|
1573
1626
|
group: groupConfig,
|
|
1574
|
-
typed,
|
|
1575
1627
|
importPath,
|
|
1576
1628
|
coercion,
|
|
1577
1629
|
inferred,
|
|
1578
1630
|
guidType,
|
|
1579
1631
|
regexType,
|
|
1580
1632
|
mini,
|
|
1581
|
-
wrapOutput,
|
|
1582
1633
|
printer
|
|
1583
1634
|
});
|
|
1584
1635
|
ctx.setResolver(userResolver ? {
|