@kubb/plugin-zod 5.0.0-beta.84 → 5.0.0-beta.85
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 +280 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +118 -137
- package/dist/index.js +281 -138
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
|
|
2
|
-
import { ast, defineGenerator, definePlugin
|
|
2
|
+
import { Resolver, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
|
|
3
3
|
import { Const, File, Type, jsxRenderer } from "kubb/jsx";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
5
5
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -41,31 +41,6 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
41
41
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
42
42
|
}
|
|
43
43
|
//#endregion
|
|
44
|
-
//#region ../../internals/utils/src/fs.ts
|
|
45
|
-
/**
|
|
46
|
-
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
47
|
-
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
48
|
-
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
49
|
-
*
|
|
50
|
-
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
51
|
-
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
52
|
-
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
53
|
-
* absolute path, letting generated files escape the configured output directory.
|
|
54
|
-
*
|
|
55
|
-
* @example Nested path from a dotted name
|
|
56
|
-
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
57
|
-
*
|
|
58
|
-
* @example PascalCase the final segment
|
|
59
|
-
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
60
|
-
*
|
|
61
|
-
* @example Suffix applied to the final segment only
|
|
62
|
-
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
63
|
-
*/
|
|
64
|
-
function toFilePath(name, caseLast = camelCase) {
|
|
65
|
-
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
66
|
-
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
67
|
-
}
|
|
68
|
-
//#endregion
|
|
69
44
|
//#region ../../internals/utils/src/reserved.ts
|
|
70
45
|
/**
|
|
71
46
|
* JavaScript and Java reserved words.
|
|
@@ -188,6 +163,188 @@ function ensureValidVarName(name) {
|
|
|
188
163
|
if (!name || isValidVarName(name)) return name;
|
|
189
164
|
return `_${name}`;
|
|
190
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
168
|
+
*
|
|
169
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
170
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
171
|
+
* deciding whether an object key needs quoting.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* isIdentifier('name') // true
|
|
176
|
+
* isIdentifier('x-total')// false
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
function isIdentifier(name) {
|
|
180
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region ../../internals/utils/src/strings.ts
|
|
184
|
+
/**
|
|
185
|
+
* Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
|
|
186
|
+
* any backslash or single quote in the content.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* singleQuote('foo') // "'foo'"
|
|
191
|
+
* singleQuote("o'clock") // "'o\\'clock'"
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
function singleQuote(value) {
|
|
195
|
+
if (value === void 0 || value === null) return "''";
|
|
196
|
+
return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
200
|
+
* Returns the string unchanged when no balanced quote pair is found.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* trimQuotes('"hello"') // 'hello'
|
|
205
|
+
* trimQuotes('hello') // 'hello'
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
function trimQuotes(text) {
|
|
209
|
+
if (text.length >= 2) {
|
|
210
|
+
const first = text[0];
|
|
211
|
+
const last = text[text.length - 1];
|
|
212
|
+
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
|
|
213
|
+
}
|
|
214
|
+
return text;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
|
|
218
|
+
*
|
|
219
|
+
* Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
|
|
220
|
+
* code matches the repo style without a formatter.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* stringify('hello') // "'hello'"
|
|
225
|
+
* stringify('"hello"') // "'hello'"
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
function stringify(value) {
|
|
229
|
+
if (value === void 0 || value === null) return "''";
|
|
230
|
+
return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
234
|
+
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
235
|
+
* Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
|
|
240
|
+
* toRegExpString('^(?im)foo', null) // '/^foo/im'
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
function toRegExpString(text, func = "RegExp") {
|
|
244
|
+
const raw = trimQuotes(text);
|
|
245
|
+
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
|
|
246
|
+
const replacementTarget = match?.[1] ?? "";
|
|
247
|
+
const matchedFlags = match?.[2];
|
|
248
|
+
const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
|
|
249
|
+
const { source, flags } = new RegExp(cleaned, matchedFlags);
|
|
250
|
+
if (func === null) return `/${source}/${flags}`;
|
|
251
|
+
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region ../../internals/utils/src/codegen.ts
|
|
255
|
+
const INDENT = " ";
|
|
256
|
+
/**
|
|
257
|
+
* Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
|
|
258
|
+
*/
|
|
259
|
+
function indentLines(text) {
|
|
260
|
+
if (!text) return "";
|
|
261
|
+
return text.split("\n").map((line) => line.trim() ? `${INDENT}${line}` : "").join("\n");
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Renders an object key, quoting it with single quotes only when it is not a valid identifier.
|
|
265
|
+
* Reserved words and globals (`name`, `class`, …) are valid bare keys and stay unquoted.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* objectKey('name') // 'name'
|
|
270
|
+
* objectKey('x-total') // "'x-total'"
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
function objectKey(name) {
|
|
274
|
+
return isIdentifier(name) ? name : singleQuote(name);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
|
|
278
|
+
* level and closing the brace at column zero. Entries that are themselves multi-line objects indent
|
|
279
|
+
* cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* buildObject(['id: z.number()', 'name: z.string()'])
|
|
284
|
+
* // '{\n id: z.number(),\n name: z.string(),\n}'
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
function buildObject(entries) {
|
|
288
|
+
if (entries.length === 0) return "{}";
|
|
289
|
+
return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on
|
|
293
|
+
* one line when no item spans multiple lines, and otherwise puts each item on its own line, indented
|
|
294
|
+
* one level with a trailing comma and the closing bracket at column zero. Used for member lists such
|
|
295
|
+
* as `z.union([…])` and `z.array([…])`.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* buildList(['z.string()', 'z.number()'])
|
|
300
|
+
* // '[z.string(), z.number()]'
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
function buildList(items, brackets = ["[", "]"]) {
|
|
304
|
+
const [open, close] = brackets;
|
|
305
|
+
if (items.length === 0) return `${open}${close}`;
|
|
306
|
+
if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
|
|
307
|
+
return `${open}\n${items.map((item) => `${indentLines(item)},`).join("\n")}\n${close}`;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
|
|
311
|
+
* is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
|
|
312
|
+
* of a recursive schema until first access.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
|
|
317
|
+
* // "get parent() { return z.lazy(() => Pet) }"
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
function lazyGetter({ name, body }) {
|
|
321
|
+
return `get ${objectKey(name)}() { return ${body} }`;
|
|
322
|
+
}
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region ../../internals/utils/src/fs.ts
|
|
325
|
+
/**
|
|
326
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
327
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
328
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
329
|
+
*
|
|
330
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
331
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
332
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
333
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
334
|
+
*
|
|
335
|
+
* @example Nested path from a dotted name
|
|
336
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
337
|
+
*
|
|
338
|
+
* @example PascalCase the final segment
|
|
339
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
340
|
+
*
|
|
341
|
+
* @example Suffix applied to the final segment only
|
|
342
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
343
|
+
*/
|
|
344
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
345
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
346
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
347
|
+
}
|
|
191
348
|
//#endregion
|
|
192
349
|
//#region ../../internals/shared/src/params.ts
|
|
193
350
|
const caseParamsCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -453,7 +610,7 @@ function isObjectComposableIntersection(node, cyclicSchemas) {
|
|
|
453
610
|
* Objects become `{}`, primitives become their string representation, strings are quoted.
|
|
454
611
|
*/
|
|
455
612
|
function formatDefault(value) {
|
|
456
|
-
if (typeof value === "string") return
|
|
613
|
+
if (typeof value === "string") return stringify(value);
|
|
457
614
|
if (typeof value === "object" && value !== null) return "{}";
|
|
458
615
|
return String(value ?? "");
|
|
459
616
|
}
|
|
@@ -489,7 +646,7 @@ function defaultLiteral(node, value) {
|
|
|
489
646
|
* Strings are quoted; numbers and booleans are emitted raw.
|
|
490
647
|
*/
|
|
491
648
|
function formatLiteral(v) {
|
|
492
|
-
if (typeof v === "string") return
|
|
649
|
+
if (typeof v === "string") return stringify(v);
|
|
493
650
|
return String(v);
|
|
494
651
|
}
|
|
495
652
|
/**
|
|
@@ -531,8 +688,7 @@ function patternKeySchemaMini({ patterns, regexType }) {
|
|
|
531
688
|
})}))`;
|
|
532
689
|
}
|
|
533
690
|
function patternKeySource({ patterns, regexType }) {
|
|
534
|
-
|
|
535
|
-
return ast.toRegExpString(source, regexFunc(regexType));
|
|
691
|
+
return toRegExpString(patterns.length === 1 ? patterns[0] : patterns.map((pattern) => `(${pattern})`).join("|"), regexFunc(regexType));
|
|
536
692
|
}
|
|
537
693
|
/**
|
|
538
694
|
* Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
|
|
@@ -555,7 +711,7 @@ function lengthConstraints({ min, max, pattern, regexType }) {
|
|
|
555
711
|
return [
|
|
556
712
|
min !== void 0 ? `.min(${min})` : "",
|
|
557
713
|
max !== void 0 ? `.max(${max})` : "",
|
|
558
|
-
pattern !== void 0 ? `.regex(${
|
|
714
|
+
pattern !== void 0 ? `.regex(${toRegExpString(pattern, regexFunc(regexType))})` : ""
|
|
559
715
|
].join("");
|
|
560
716
|
}
|
|
561
717
|
/**
|
|
@@ -577,7 +733,7 @@ function lengthChecksMini({ min, max, pattern, regexType }) {
|
|
|
577
733
|
const checks = [];
|
|
578
734
|
if (min !== void 0) checks.push(`z.minLength(${min})`);
|
|
579
735
|
if (max !== void 0) checks.push(`z.maxLength(${max})`);
|
|
580
|
-
if (pattern !== void 0) checks.push(`z.regex(${
|
|
736
|
+
if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, regexFunc(regexType))})`);
|
|
581
737
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
582
738
|
}
|
|
583
739
|
/**
|
|
@@ -593,7 +749,7 @@ function applyModifiers({ value, schema, nullable, optional, nullish, defaultVal
|
|
|
593
749
|
})();
|
|
594
750
|
const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
|
|
595
751
|
const withDefault = literal !== null ? `${withModifier}.default(${literal})` : withModifier;
|
|
596
|
-
const withDescription = description ? `${withDefault}.describe(${
|
|
752
|
+
const withDescription = description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
|
|
597
753
|
return examples?.length ? `${withDescription}.meta({ examples: [${examples.map(formatDefault).join(", ")}] })` : withDescription;
|
|
598
754
|
}
|
|
599
755
|
function modifierDepth(schema) {
|
|
@@ -664,7 +820,7 @@ function buildZodObjectShape(ctx, node) {
|
|
|
664
820
|
const objectNode = ast.narrowSchema(node, "object");
|
|
665
821
|
if (!objectNode) return "{}";
|
|
666
822
|
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
667
|
-
|
|
823
|
+
return buildObject(ast.mapSchemaProperties(objectNode, (schema) => {
|
|
668
824
|
const hasSelfRef = isCyclic(schema);
|
|
669
825
|
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
670
826
|
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
@@ -685,12 +841,11 @@ function buildZodObjectShape(ctx, node) {
|
|
|
685
841
|
description: descriptionToApply,
|
|
686
842
|
examples: meta.examples
|
|
687
843
|
});
|
|
688
|
-
return isCyclic(schema) ?
|
|
844
|
+
return isCyclic(schema) ? lazyGetter({
|
|
689
845
|
name: propName,
|
|
690
846
|
body: value
|
|
691
|
-
}) : `${
|
|
692
|
-
});
|
|
693
|
-
return ast.buildObject(entries);
|
|
847
|
+
}) : `${objectKey(propName)}: ${value}`;
|
|
848
|
+
}));
|
|
694
849
|
}
|
|
695
850
|
/**
|
|
696
851
|
* Zod v4 printer built with `definePrinter`.
|
|
@@ -784,7 +939,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
784
939
|
if (!node.name) return null;
|
|
785
940
|
const refName = node.ref ? this.options.nameMapping?.get(node.ref) ?? ast.extractRefName(node.ref) ?? node.name : node.name;
|
|
786
941
|
const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
|
|
787
|
-
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.
|
|
942
|
+
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
|
|
788
943
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
|
|
789
944
|
return resolvedName;
|
|
790
945
|
},
|
|
@@ -823,8 +978,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
823
978
|
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
|
|
824
979
|
},
|
|
825
980
|
tuple(node) {
|
|
826
|
-
|
|
827
|
-
return `z.tuple(${ast.buildList(items)})`;
|
|
981
|
+
return `z.tuple(${buildList(ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
|
|
828
982
|
},
|
|
829
983
|
union(node) {
|
|
830
984
|
const nodeMembers = node.members ?? [];
|
|
@@ -832,8 +986,8 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
832
986
|
if (members.length === 0) return "";
|
|
833
987
|
if (members.length === 1) return members[0];
|
|
834
988
|
const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
835
|
-
if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${
|
|
836
|
-
return `z.union(${
|
|
989
|
+
if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
990
|
+
return `z.union(${buildList(members)})`;
|
|
837
991
|
},
|
|
838
992
|
intersection(node) {
|
|
839
993
|
const members = node.members ?? [];
|
|
@@ -905,7 +1059,7 @@ function buildZodMiniObjectShape(ctx, node) {
|
|
|
905
1059
|
const objectNode = ast.narrowSchema(node, "object");
|
|
906
1060
|
if (!objectNode) return "{}";
|
|
907
1061
|
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
908
|
-
|
|
1062
|
+
return buildObject(ast.mapSchemaProperties(objectNode, (schema) => {
|
|
909
1063
|
const hasSelfRef = isCyclic(schema);
|
|
910
1064
|
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
911
1065
|
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
@@ -923,12 +1077,11 @@ function buildZodMiniObjectShape(ctx, node) {
|
|
|
923
1077
|
nullish: schema.nullish,
|
|
924
1078
|
defaultValue: meta.default
|
|
925
1079
|
});
|
|
926
|
-
return isCyclic(schema) ?
|
|
1080
|
+
return isCyclic(schema) ? lazyGetter({
|
|
927
1081
|
name: propName,
|
|
928
1082
|
body: value
|
|
929
|
-
}) : `${
|
|
930
|
-
});
|
|
931
|
-
return ast.buildObject(entries);
|
|
1083
|
+
}) : `${objectKey(propName)}: ${value}`;
|
|
1084
|
+
}));
|
|
932
1085
|
}
|
|
933
1086
|
/**
|
|
934
1087
|
* Zod v4 Mini printer built with `definePrinter`.
|
|
@@ -1013,7 +1166,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1013
1166
|
ref(node) {
|
|
1014
1167
|
if (!node.name) return null;
|
|
1015
1168
|
const refName = node.ref ? this.options.nameMapping?.get(node.ref) ?? ast.extractRefName(node.ref) ?? node.name : node.name;
|
|
1016
|
-
const resolvedName = node.ref ? this.options.resolver?.
|
|
1169
|
+
const resolvedName = node.ref ? this.options.resolver?.name(refName) ?? refName : node.name;
|
|
1017
1170
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
|
|
1018
1171
|
return resolvedName;
|
|
1019
1172
|
},
|
|
@@ -1050,8 +1203,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1050
1203
|
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
|
|
1051
1204
|
},
|
|
1052
1205
|
tuple(node) {
|
|
1053
|
-
|
|
1054
|
-
return `z.tuple(${ast.buildList(items)})`;
|
|
1206
|
+
return `z.tuple(${buildList(ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
|
|
1055
1207
|
},
|
|
1056
1208
|
union(node) {
|
|
1057
1209
|
const nodeMembers = node.members ?? [];
|
|
@@ -1059,8 +1211,8 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1059
1211
|
if (members.length === 0) return "";
|
|
1060
1212
|
if (members.length === 1) return members[0];
|
|
1061
1213
|
const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1062
|
-
if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${
|
|
1063
|
-
return `z.union(${
|
|
1214
|
+
if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
1215
|
+
return `z.union(${buildList(members)})`;
|
|
1064
1216
|
},
|
|
1065
1217
|
intersection(node) {
|
|
1066
1218
|
const members = node.members ?? [];
|
|
@@ -1180,22 +1332,20 @@ const zodGenerator = defineGenerator({
|
|
|
1180
1332
|
const hasCodec = !mini && containsCodec(node);
|
|
1181
1333
|
const codecRefNames = new Set(hasCodec ? collectCodecRefNames(node) : []);
|
|
1182
1334
|
const importEntries = adapter.getImports(node, (schemaName) => ({
|
|
1183
|
-
name: resolver.
|
|
1184
|
-
path: resolver.
|
|
1335
|
+
name: resolver.name(schemaName),
|
|
1336
|
+
path: resolver.file({
|
|
1185
1337
|
name: schemaName,
|
|
1186
|
-
extname: ".ts"
|
|
1187
|
-
}, {
|
|
1338
|
+
extname: ".ts",
|
|
1188
1339
|
root,
|
|
1189
1340
|
output,
|
|
1190
1341
|
group: group ?? void 0
|
|
1191
1342
|
}).path
|
|
1192
1343
|
}));
|
|
1193
1344
|
const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
|
|
1194
|
-
name: [resolver.
|
|
1195
|
-
path: resolver.
|
|
1345
|
+
name: [resolver.schema.inputName(schemaName)],
|
|
1346
|
+
path: resolver.file({
|
|
1196
1347
|
name: schemaName,
|
|
1197
|
-
extname: ".ts"
|
|
1198
|
-
}, {
|
|
1348
|
+
extname: ".ts",
|
|
1199
1349
|
root,
|
|
1200
1350
|
output,
|
|
1201
1351
|
group: group ?? void 0
|
|
@@ -1209,17 +1359,16 @@ const zodGenerator = defineGenerator({
|
|
|
1209
1359
|
return true;
|
|
1210
1360
|
});
|
|
1211
1361
|
const meta = {
|
|
1212
|
-
name: resolver.
|
|
1213
|
-
file: resolver.
|
|
1362
|
+
name: resolver.name(node.name),
|
|
1363
|
+
file: resolver.file({
|
|
1214
1364
|
name: node.name,
|
|
1215
|
-
extname: ".ts"
|
|
1216
|
-
}, {
|
|
1365
|
+
extname: ".ts",
|
|
1217
1366
|
root,
|
|
1218
1367
|
output,
|
|
1219
1368
|
group: group ?? void 0
|
|
1220
1369
|
})
|
|
1221
1370
|
};
|
|
1222
|
-
const inferTypeName = inferred ? resolver.
|
|
1371
|
+
const inferTypeName = inferred ? resolver.schema.typeName(node.name) : null;
|
|
1223
1372
|
const nameMapping = adapter.options.nameMapping;
|
|
1224
1373
|
const stdPrinters = mini ? null : getStdPrinters(resolver, {
|
|
1225
1374
|
coercion,
|
|
@@ -1241,7 +1390,7 @@ const zodGenerator = defineGenerator({
|
|
|
1241
1390
|
baseName: meta.file.baseName,
|
|
1242
1391
|
path: meta.file.path,
|
|
1243
1392
|
meta: meta.file.meta,
|
|
1244
|
-
banner: resolver.
|
|
1393
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1245
1394
|
output,
|
|
1246
1395
|
config,
|
|
1247
1396
|
file: {
|
|
@@ -1249,7 +1398,7 @@ const zodGenerator = defineGenerator({
|
|
|
1249
1398
|
baseName: meta.file.baseName
|
|
1250
1399
|
}
|
|
1251
1400
|
}),
|
|
1252
|
-
footer: resolver.
|
|
1401
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1253
1402
|
output,
|
|
1254
1403
|
config,
|
|
1255
1404
|
file: {
|
|
@@ -1280,10 +1429,10 @@ const zodGenerator = defineGenerator({
|
|
|
1280
1429
|
cyclic: cyclicSchemas.has(node.name)
|
|
1281
1430
|
}),
|
|
1282
1431
|
hasCodec && stdPrinters && /* @__PURE__ */ jsx(Zod, {
|
|
1283
|
-
name: resolver.
|
|
1432
|
+
name: resolver.schema.inputName(node.name),
|
|
1284
1433
|
node,
|
|
1285
1434
|
printer: stdPrinters.input,
|
|
1286
|
-
inferTypeName: inferred ? resolver.
|
|
1435
|
+
inferTypeName: inferred ? resolver.schema.inputTypeName(node.name) : null,
|
|
1287
1436
|
cyclic: cyclicSchemas.has(node.name)
|
|
1288
1437
|
})
|
|
1289
1438
|
]
|
|
@@ -1296,12 +1445,11 @@ const zodGenerator = defineGenerator({
|
|
|
1296
1445
|
const dateType = adapter.options.dateType;
|
|
1297
1446
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
1298
1447
|
const params = caseParams(node.parameters, "camelcase");
|
|
1299
|
-
const meta = { file: resolver.
|
|
1448
|
+
const meta = { file: resolver.file({
|
|
1300
1449
|
name: node.operationId,
|
|
1301
1450
|
extname: ".ts",
|
|
1302
1451
|
tag: node.tags[0] ?? "default",
|
|
1303
|
-
path: node.path
|
|
1304
|
-
}, {
|
|
1452
|
+
path: node.path,
|
|
1305
1453
|
root,
|
|
1306
1454
|
output,
|
|
1307
1455
|
group: group ?? void 0
|
|
@@ -1310,14 +1458,13 @@ const zodGenerator = defineGenerator({
|
|
|
1310
1458
|
const nameMapping = adapter.options.nameMapping;
|
|
1311
1459
|
function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
|
|
1312
1460
|
if (!schema) return null;
|
|
1313
|
-
const inferTypeName = inferred ? resolver.
|
|
1461
|
+
const inferTypeName = inferred ? resolver.schema.type(name) : null;
|
|
1314
1462
|
const codecRefNames = direction === "input" && !mini ? new Set(collectCodecRefNames(schema)) : null;
|
|
1315
1463
|
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
1316
|
-
name: codecRefNames?.has(schemaName) ? resolver.
|
|
1317
|
-
path: resolver.
|
|
1464
|
+
name: codecRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName),
|
|
1465
|
+
path: resolver.file({
|
|
1318
1466
|
name: schemaName,
|
|
1319
|
-
extname: ".ts"
|
|
1320
|
-
}, {
|
|
1467
|
+
extname: ".ts",
|
|
1321
1468
|
root,
|
|
1322
1469
|
output,
|
|
1323
1470
|
group: group ?? void 0
|
|
@@ -1395,30 +1542,30 @@ const zodGenerator = defineGenerator({
|
|
|
1395
1542
|
}
|
|
1396
1543
|
const paramSchemas = params.map((param) => renderSchemaEntry({
|
|
1397
1544
|
schema: param.schema,
|
|
1398
|
-
name: resolver.
|
|
1545
|
+
name: resolver.param.name(node, param),
|
|
1399
1546
|
direction: "input"
|
|
1400
1547
|
}));
|
|
1401
1548
|
const responseSchemas = node.responses.map((res) => {
|
|
1402
1549
|
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1403
|
-
if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.
|
|
1550
|
+
if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.response.status(node, res.statusCode));
|
|
1404
1551
|
const primary = variants[0] ?? res.content?.[0];
|
|
1405
1552
|
return renderSchemaEntry({
|
|
1406
1553
|
schema: primary?.schema ?? null,
|
|
1407
|
-
name: resolver.
|
|
1554
|
+
name: resolver.response.status(node, res.statusCode),
|
|
1408
1555
|
keysToOmit: primary?.keysToOmit
|
|
1409
1556
|
});
|
|
1410
1557
|
});
|
|
1411
1558
|
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
1412
1559
|
const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
|
|
1413
1560
|
const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
|
|
1414
|
-
const responseUnionName = resolver.
|
|
1561
|
+
const responseUnionName = resolver.response.response(node);
|
|
1415
1562
|
if (new Set(successResponsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
|
|
1416
|
-
name: resolver.
|
|
1563
|
+
name: resolver.name(schemaName),
|
|
1417
1564
|
path: ""
|
|
1418
1565
|
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
|
|
1419
1566
|
const members = successResponsesWithSchema.map((res) => ast.factory.createSchema({
|
|
1420
1567
|
type: "ref",
|
|
1421
|
-
name: resolver.
|
|
1568
|
+
name: resolver.response.status(node, res.statusCode)
|
|
1422
1569
|
}));
|
|
1423
1570
|
if (members.length === 0) return renderSchemaEntry({
|
|
1424
1571
|
schema: ast.factory.createSchema({ type: "unknown" }),
|
|
@@ -1434,14 +1581,14 @@ const zodGenerator = defineGenerator({
|
|
|
1434
1581
|
})() : null;
|
|
1435
1582
|
const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
|
|
1436
1583
|
const errorUnionSchema = errorResponsesWithSchema.length > 0 ? (() => {
|
|
1437
|
-
const errorUnionName = resolver.
|
|
1584
|
+
const errorUnionName = resolver.response.error(node);
|
|
1438
1585
|
if (new Set(errorResponsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
|
|
1439
|
-
name: resolver.
|
|
1586
|
+
name: resolver.name(schemaName),
|
|
1440
1587
|
path: ""
|
|
1441
1588
|
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(errorUnionName)) return null;
|
|
1442
1589
|
const members = errorResponsesWithSchema.map((res) => ast.factory.createSchema({
|
|
1443
1590
|
type: "ref",
|
|
1444
|
-
name: resolver.
|
|
1591
|
+
name: resolver.response.status(node, res.statusCode)
|
|
1445
1592
|
}));
|
|
1446
1593
|
return renderSchemaEntry({
|
|
1447
1594
|
schema: members.length === 1 ? members[0] : ast.factory.createSchema({
|
|
@@ -1462,12 +1609,12 @@ const zodGenerator = defineGenerator({
|
|
|
1462
1609
|
...entry.schema,
|
|
1463
1610
|
description: node.requestBody.description ?? entry.schema.description
|
|
1464
1611
|
},
|
|
1465
|
-
name: resolver.
|
|
1612
|
+
name: resolver.response.body(node),
|
|
1466
1613
|
keysToOmit: entry.keysToOmit,
|
|
1467
1614
|
direction: "input"
|
|
1468
1615
|
});
|
|
1469
1616
|
}
|
|
1470
|
-
return buildContentTypeVariants(requestBodyContent, resolver.
|
|
1617
|
+
return buildContentTypeVariants(requestBodyContent, resolver.response.body(node), (schema) => ({
|
|
1471
1618
|
...schema,
|
|
1472
1619
|
description: node.requestBody.description ?? schema.description
|
|
1473
1620
|
}), "input");
|
|
@@ -1476,7 +1623,7 @@ const zodGenerator = defineGenerator({
|
|
|
1476
1623
|
baseName: meta.file.baseName,
|
|
1477
1624
|
path: meta.file.path,
|
|
1478
1625
|
meta: meta.file.meta,
|
|
1479
|
-
banner: resolver.
|
|
1626
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1480
1627
|
output,
|
|
1481
1628
|
config,
|
|
1482
1629
|
file: {
|
|
@@ -1484,7 +1631,7 @@ const zodGenerator = defineGenerator({
|
|
|
1484
1631
|
baseName: meta.file.baseName
|
|
1485
1632
|
}
|
|
1486
1633
|
}),
|
|
1487
|
-
footer: resolver.
|
|
1634
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1488
1635
|
output,
|
|
1489
1636
|
config,
|
|
1490
1637
|
file: {
|
|
@@ -1520,64 +1667,63 @@ const zodGenerator = defineGenerator({
|
|
|
1520
1667
|
* ```ts
|
|
1521
1668
|
* import { resolverZod } from '@kubb/plugin-zod'
|
|
1522
1669
|
*
|
|
1523
|
-
* resolverZod.
|
|
1524
|
-
* resolverZod.
|
|
1670
|
+
* resolverZod.name('list pets') // 'listPetsSchema'
|
|
1671
|
+
* resolverZod.schema.typeName('pet') // 'PetSchemaType'
|
|
1525
1672
|
* ```
|
|
1526
1673
|
*/
|
|
1527
|
-
const resolverZod =
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
},
|
|
1538
|
-
resolveSchemaTypeName(name) {
|
|
1674
|
+
const resolverZod = createResolver({
|
|
1675
|
+
pluginName: "plugin-zod",
|
|
1676
|
+
name(name) {
|
|
1677
|
+
return ensureValidVarName(camelCase(name, { suffix: "schema" }));
|
|
1678
|
+
},
|
|
1679
|
+
file: { baseName({ name, extname }) {
|
|
1680
|
+
return `${toFilePath(name, (part) => camelCase(part, { suffix: "schema" }))}${extname}`;
|
|
1681
|
+
} },
|
|
1682
|
+
schema: {
|
|
1683
|
+
typeName(name) {
|
|
1539
1684
|
return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
|
|
1540
1685
|
},
|
|
1541
|
-
|
|
1542
|
-
return this.resolveSchemaName(`${name} input`);
|
|
1543
|
-
},
|
|
1544
|
-
resolveInputSchemaTypeName(name) {
|
|
1545
|
-
return this.resolveSchemaTypeName(`${name} input`);
|
|
1546
|
-
},
|
|
1547
|
-
resolveTypeName(name) {
|
|
1686
|
+
type(name) {
|
|
1548
1687
|
return ensureValidVarName(pascalCase(name, { suffix: "type" }));
|
|
1549
1688
|
},
|
|
1550
|
-
|
|
1551
|
-
return this.
|
|
1552
|
-
},
|
|
1553
|
-
resolveParamName(node, param) {
|
|
1554
|
-
return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
|
|
1689
|
+
inputName(name) {
|
|
1690
|
+
return this.name(`${name} input`);
|
|
1555
1691
|
},
|
|
1556
|
-
|
|
1557
|
-
return this.
|
|
1692
|
+
inputTypeName(name) {
|
|
1693
|
+
return this.schema.typeName(`${name} input`);
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
param: {
|
|
1697
|
+
name(node, param) {
|
|
1698
|
+
return this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
1558
1699
|
},
|
|
1559
|
-
|
|
1560
|
-
return this.
|
|
1700
|
+
path(node, param) {
|
|
1701
|
+
return this.param.name(node, param);
|
|
1561
1702
|
},
|
|
1562
|
-
|
|
1563
|
-
return this.
|
|
1703
|
+
query(node, param) {
|
|
1704
|
+
return this.param.name(node, param);
|
|
1564
1705
|
},
|
|
1565
|
-
|
|
1566
|
-
return this.
|
|
1706
|
+
headers(node, param) {
|
|
1707
|
+
return this.param.name(node, param);
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1710
|
+
response: {
|
|
1711
|
+
status(node, statusCode) {
|
|
1712
|
+
return this.name(`${node.operationId} Status ${statusCode}`);
|
|
1567
1713
|
},
|
|
1568
|
-
|
|
1569
|
-
return this.
|
|
1714
|
+
body(node) {
|
|
1715
|
+
return this.name(`${node.operationId} Body`);
|
|
1570
1716
|
},
|
|
1571
|
-
|
|
1572
|
-
return this.
|
|
1717
|
+
responses(node) {
|
|
1718
|
+
return this.name(`${node.operationId} Responses`);
|
|
1573
1719
|
},
|
|
1574
|
-
|
|
1575
|
-
return this.
|
|
1720
|
+
response(node) {
|
|
1721
|
+
return this.name(`${node.operationId} Response`);
|
|
1576
1722
|
},
|
|
1577
|
-
|
|
1578
|
-
return this.
|
|
1723
|
+
error(node) {
|
|
1724
|
+
return this.name(`${node.operationId} Error`);
|
|
1579
1725
|
}
|
|
1580
|
-
}
|
|
1726
|
+
}
|
|
1581
1727
|
});
|
|
1582
1728
|
//#endregion
|
|
1583
1729
|
//#region src/plugin.ts
|
|
@@ -1636,10 +1782,7 @@ const pluginZod = definePlugin((options) => {
|
|
|
1636
1782
|
mini,
|
|
1637
1783
|
printer
|
|
1638
1784
|
});
|
|
1639
|
-
ctx.setResolver(userResolver ?
|
|
1640
|
-
...resolverZod,
|
|
1641
|
-
...userResolver
|
|
1642
|
-
} : resolverZod);
|
|
1785
|
+
ctx.setResolver(userResolver ? Resolver.merge(resolverZod, userResolver) : resolverZod);
|
|
1643
1786
|
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
1644
1787
|
ctx.addGenerator(zodGenerator);
|
|
1645
1788
|
} }
|