@kubb/plugin-zod 5.0.0-beta.84 → 5.0.0-beta.86
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 +306 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +118 -137
- package/dist/index.js +307 -173
- 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.
|
|
@@ -166,7 +141,7 @@ const reservedWords = /* @__PURE__ */ new Set([
|
|
|
166
141
|
*/
|
|
167
142
|
function isValidVarName(name) {
|
|
168
143
|
if (!name || reservedWords.has(name)) return false;
|
|
169
|
-
return
|
|
144
|
+
return isIdentifier(name);
|
|
170
145
|
}
|
|
171
146
|
/**
|
|
172
147
|
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
@@ -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
|
|
@@ -1393,64 +1540,55 @@ const zodGenerator = defineGenerator({
|
|
|
1393
1540
|
direction
|
|
1394
1541
|
})] });
|
|
1395
1542
|
}
|
|
1543
|
+
function buildResponseUnion({ responses, name, fallbackUnknown }) {
|
|
1544
|
+
if (new Set(responses.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
|
|
1545
|
+
name: resolver.name(schemaName),
|
|
1546
|
+
path: ""
|
|
1547
|
+
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(name)) return null;
|
|
1548
|
+
const members = responses.map((res) => ast.factory.createSchema({
|
|
1549
|
+
type: "ref",
|
|
1550
|
+
name: resolver.response.status(node, res.statusCode)
|
|
1551
|
+
}));
|
|
1552
|
+
if (fallbackUnknown && members.length === 0) return renderSchemaEntry({
|
|
1553
|
+
schema: ast.factory.createSchema({ type: "unknown" }),
|
|
1554
|
+
name
|
|
1555
|
+
});
|
|
1556
|
+
return renderSchemaEntry({
|
|
1557
|
+
schema: members.length === 1 ? members[0] : ast.factory.createSchema({
|
|
1558
|
+
type: "union",
|
|
1559
|
+
members
|
|
1560
|
+
}),
|
|
1561
|
+
name
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1396
1564
|
const paramSchemas = params.map((param) => renderSchemaEntry({
|
|
1397
1565
|
schema: param.schema,
|
|
1398
|
-
name: resolver.
|
|
1566
|
+
name: resolver.param.name(node, param),
|
|
1399
1567
|
direction: "input"
|
|
1400
1568
|
}));
|
|
1401
1569
|
const responseSchemas = node.responses.map((res) => {
|
|
1402
1570
|
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1403
|
-
if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.
|
|
1571
|
+
if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.response.status(node, res.statusCode));
|
|
1404
1572
|
const primary = variants[0] ?? res.content?.[0];
|
|
1405
1573
|
return renderSchemaEntry({
|
|
1406
1574
|
schema: primary?.schema ?? null,
|
|
1407
|
-
name: resolver.
|
|
1575
|
+
name: resolver.response.status(node, res.statusCode),
|
|
1408
1576
|
keysToOmit: primary?.keysToOmit
|
|
1409
1577
|
});
|
|
1410
1578
|
});
|
|
1411
1579
|
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
1412
1580
|
const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
|
|
1413
|
-
const responseUnionSchema = responsesWithSchema.length > 0 ? (
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
|
|
1419
|
-
const members = successResponsesWithSchema.map((res) => ast.factory.createSchema({
|
|
1420
|
-
type: "ref",
|
|
1421
|
-
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
1422
|
-
}));
|
|
1423
|
-
if (members.length === 0) return renderSchemaEntry({
|
|
1424
|
-
schema: ast.factory.createSchema({ type: "unknown" }),
|
|
1425
|
-
name: responseUnionName
|
|
1426
|
-
});
|
|
1427
|
-
return renderSchemaEntry({
|
|
1428
|
-
schema: members.length === 1 ? members[0] : ast.factory.createSchema({
|
|
1429
|
-
type: "union",
|
|
1430
|
-
members
|
|
1431
|
-
}),
|
|
1432
|
-
name: responseUnionName
|
|
1433
|
-
});
|
|
1434
|
-
})() : null;
|
|
1581
|
+
const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
|
|
1582
|
+
responses: successResponsesWithSchema,
|
|
1583
|
+
name: resolver.response.response(node),
|
|
1584
|
+
fallbackUnknown: true
|
|
1585
|
+
}) : null;
|
|
1435
1586
|
const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
|
|
1436
|
-
const errorUnionSchema = errorResponsesWithSchema.length > 0 ? (
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(errorUnionName)) return null;
|
|
1442
|
-
const members = errorResponsesWithSchema.map((res) => ast.factory.createSchema({
|
|
1443
|
-
type: "ref",
|
|
1444
|
-
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
1445
|
-
}));
|
|
1446
|
-
return renderSchemaEntry({
|
|
1447
|
-
schema: members.length === 1 ? members[0] : ast.factory.createSchema({
|
|
1448
|
-
type: "union",
|
|
1449
|
-
members
|
|
1450
|
-
}),
|
|
1451
|
-
name: errorUnionName
|
|
1452
|
-
});
|
|
1453
|
-
})() : null;
|
|
1587
|
+
const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
|
|
1588
|
+
responses: errorResponsesWithSchema,
|
|
1589
|
+
name: resolver.response.error(node),
|
|
1590
|
+
fallbackUnknown: false
|
|
1591
|
+
}) : null;
|
|
1454
1592
|
const requestBodyContent = node.requestBody?.content ?? [];
|
|
1455
1593
|
const requestSchema = (() => {
|
|
1456
1594
|
if (requestBodyContent.length === 0) return null;
|
|
@@ -1462,12 +1600,12 @@ const zodGenerator = defineGenerator({
|
|
|
1462
1600
|
...entry.schema,
|
|
1463
1601
|
description: node.requestBody.description ?? entry.schema.description
|
|
1464
1602
|
},
|
|
1465
|
-
name: resolver.
|
|
1603
|
+
name: resolver.response.body(node),
|
|
1466
1604
|
keysToOmit: entry.keysToOmit,
|
|
1467
1605
|
direction: "input"
|
|
1468
1606
|
});
|
|
1469
1607
|
}
|
|
1470
|
-
return buildContentTypeVariants(requestBodyContent, resolver.
|
|
1608
|
+
return buildContentTypeVariants(requestBodyContent, resolver.response.body(node), (schema) => ({
|
|
1471
1609
|
...schema,
|
|
1472
1610
|
description: node.requestBody.description ?? schema.description
|
|
1473
1611
|
}), "input");
|
|
@@ -1476,7 +1614,7 @@ const zodGenerator = defineGenerator({
|
|
|
1476
1614
|
baseName: meta.file.baseName,
|
|
1477
1615
|
path: meta.file.path,
|
|
1478
1616
|
meta: meta.file.meta,
|
|
1479
|
-
banner: resolver.
|
|
1617
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1480
1618
|
output,
|
|
1481
1619
|
config,
|
|
1482
1620
|
file: {
|
|
@@ -1484,7 +1622,7 @@ const zodGenerator = defineGenerator({
|
|
|
1484
1622
|
baseName: meta.file.baseName
|
|
1485
1623
|
}
|
|
1486
1624
|
}),
|
|
1487
|
-
footer: resolver.
|
|
1625
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1488
1626
|
output,
|
|
1489
1627
|
config,
|
|
1490
1628
|
file: {
|
|
@@ -1520,64 +1658,63 @@ const zodGenerator = defineGenerator({
|
|
|
1520
1658
|
* ```ts
|
|
1521
1659
|
* import { resolverZod } from '@kubb/plugin-zod'
|
|
1522
1660
|
*
|
|
1523
|
-
* resolverZod.
|
|
1524
|
-
* resolverZod.
|
|
1661
|
+
* resolverZod.name('list pets') // 'listPetsSchema'
|
|
1662
|
+
* resolverZod.schema.typeName('pet') // 'PetSchemaType'
|
|
1525
1663
|
* ```
|
|
1526
1664
|
*/
|
|
1527
|
-
const resolverZod =
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
},
|
|
1538
|
-
resolveSchemaTypeName(name) {
|
|
1665
|
+
const resolverZod = createResolver({
|
|
1666
|
+
pluginName: "plugin-zod",
|
|
1667
|
+
name(name) {
|
|
1668
|
+
return ensureValidVarName(camelCase(name, { suffix: "schema" }));
|
|
1669
|
+
},
|
|
1670
|
+
file: { baseName({ name, extname }) {
|
|
1671
|
+
return `${toFilePath(name, (part) => camelCase(part, { suffix: "schema" }))}${extname}`;
|
|
1672
|
+
} },
|
|
1673
|
+
schema: {
|
|
1674
|
+
typeName(name) {
|
|
1539
1675
|
return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
|
|
1540
1676
|
},
|
|
1541
|
-
|
|
1542
|
-
return this.resolveSchemaName(`${name} input`);
|
|
1543
|
-
},
|
|
1544
|
-
resolveInputSchemaTypeName(name) {
|
|
1545
|
-
return this.resolveSchemaTypeName(`${name} input`);
|
|
1546
|
-
},
|
|
1547
|
-
resolveTypeName(name) {
|
|
1677
|
+
type(name) {
|
|
1548
1678
|
return ensureValidVarName(pascalCase(name, { suffix: "type" }));
|
|
1549
1679
|
},
|
|
1550
|
-
|
|
1551
|
-
return this.
|
|
1680
|
+
inputName(name) {
|
|
1681
|
+
return this.name(`${name} input`);
|
|
1552
1682
|
},
|
|
1553
|
-
|
|
1554
|
-
return this.
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1683
|
+
inputTypeName(name) {
|
|
1684
|
+
return this.schema.typeName(`${name} input`);
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
param: {
|
|
1688
|
+
name(node, param) {
|
|
1689
|
+
return this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
1558
1690
|
},
|
|
1559
|
-
|
|
1560
|
-
return this.
|
|
1691
|
+
path(node, param) {
|
|
1692
|
+
return this.param.name(node, param);
|
|
1561
1693
|
},
|
|
1562
|
-
|
|
1563
|
-
return this.
|
|
1694
|
+
query(node, param) {
|
|
1695
|
+
return this.param.name(node, param);
|
|
1564
1696
|
},
|
|
1565
|
-
|
|
1566
|
-
return this.
|
|
1697
|
+
headers(node, param) {
|
|
1698
|
+
return this.param.name(node, param);
|
|
1699
|
+
}
|
|
1700
|
+
},
|
|
1701
|
+
response: {
|
|
1702
|
+
status(node, statusCode) {
|
|
1703
|
+
return this.name(`${node.operationId} Status ${statusCode}`);
|
|
1567
1704
|
},
|
|
1568
|
-
|
|
1569
|
-
return this.
|
|
1705
|
+
body(node) {
|
|
1706
|
+
return this.name(`${node.operationId} Body`);
|
|
1570
1707
|
},
|
|
1571
|
-
|
|
1572
|
-
return this.
|
|
1708
|
+
responses(node) {
|
|
1709
|
+
return this.name(`${node.operationId} Responses`);
|
|
1573
1710
|
},
|
|
1574
|
-
|
|
1575
|
-
return this.
|
|
1711
|
+
response(node) {
|
|
1712
|
+
return this.name(`${node.operationId} Response`);
|
|
1576
1713
|
},
|
|
1577
|
-
|
|
1578
|
-
return this.
|
|
1714
|
+
error(node) {
|
|
1715
|
+
return this.name(`${node.operationId} Error`);
|
|
1579
1716
|
}
|
|
1580
|
-
}
|
|
1717
|
+
}
|
|
1581
1718
|
});
|
|
1582
1719
|
//#endregion
|
|
1583
1720
|
//#region src/plugin.ts
|
|
@@ -1636,10 +1773,7 @@ const pluginZod = definePlugin((options) => {
|
|
|
1636
1773
|
mini,
|
|
1637
1774
|
printer
|
|
1638
1775
|
});
|
|
1639
|
-
ctx.setResolver(userResolver ?
|
|
1640
|
-
...resolverZod,
|
|
1641
|
-
...userResolver
|
|
1642
|
-
} : resolverZod);
|
|
1776
|
+
ctx.setResolver(userResolver ? Resolver.merge(resolverZod, userResolver) : resolverZod);
|
|
1643
1777
|
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
1644
1778
|
ctx.addGenerator(zodGenerator);
|
|
1645
1779
|
} }
|