@kubb/plugin-zod 5.0.0-beta.4 → 5.0.0-beta.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -22
- package/dist/index.cjs +684 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +115 -49
- package/dist/index.js +673 -296
- package/dist/index.js.map +1 -1
- package/package.json +13 -23
- package/src/components/Operations.tsx +7 -6
- package/src/components/Zod.tsx +1 -1
- package/src/generators/zodGenerator.tsx +213 -62
- package/src/plugin.ts +24 -21
- package/src/printers/printerZod.ts +131 -110
- package/src/printers/printerZodMini.ts +82 -87
- package/src/resolvers/resolverZod.ts +33 -19
- package/src/types.ts +64 -32
- package/src/utils.ts +114 -37
- package/extension.yaml +0 -502
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import "./chunk
|
|
1
|
+
import { t as __name } from "./chunk-C0LytTxp.js";
|
|
2
2
|
import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
|
|
3
|
+
import { buildList, buildObject, extractRefName, objectKey, stringify, stringifyObject, toRegExpString } from "@kubb/ast/utils";
|
|
3
4
|
import { Const, File, Type, jsxRenderer } from "@kubb/renderer-jsx";
|
|
4
5
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
5
6
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -13,117 +14,261 @@ import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
|
13
14
|
function toCamelOrPascal(text, pascal) {
|
|
14
15
|
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
15
16
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
16
|
-
|
|
17
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
17
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
18
18
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
22
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
23
|
-
* Segments are joined with `/` to form a file path.
|
|
24
|
-
*
|
|
25
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
26
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
27
|
-
*/
|
|
28
|
-
function applyToFileParts(text, transformPart) {
|
|
29
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
30
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
21
|
* Converts `text` to camelCase.
|
|
34
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
35
22
|
*
|
|
36
|
-
* @example
|
|
37
|
-
* camelCase('hello-world')
|
|
38
|
-
*
|
|
23
|
+
* @example Word boundaries
|
|
24
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
25
|
+
*
|
|
26
|
+
* @example With a prefix
|
|
27
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
39
28
|
*/
|
|
40
|
-
function camelCase(text, {
|
|
41
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
42
|
-
prefix,
|
|
43
|
-
suffix
|
|
44
|
-
} : {}));
|
|
29
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
45
30
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
46
31
|
}
|
|
47
32
|
/**
|
|
48
33
|
* Converts `text` to PascalCase.
|
|
49
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
50
34
|
*
|
|
51
|
-
* @example
|
|
52
|
-
* pascalCase('hello-world')
|
|
53
|
-
*
|
|
35
|
+
* @example Word boundaries
|
|
36
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
37
|
+
*
|
|
38
|
+
* @example With a suffix
|
|
39
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
54
40
|
*/
|
|
55
|
-
function pascalCase(text, {
|
|
56
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
57
|
-
prefix,
|
|
58
|
-
suffix
|
|
59
|
-
}) : camelCase(part));
|
|
41
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
60
42
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
61
43
|
}
|
|
62
44
|
//#endregion
|
|
63
|
-
//#region ../../internals/utils/src/
|
|
45
|
+
//#region ../../internals/utils/src/fs.ts
|
|
64
46
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
47
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
48
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
49
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
67
50
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
51
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
52
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
53
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
54
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
55
|
+
*
|
|
56
|
+
* @example Nested path from a dotted name
|
|
57
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
58
|
+
*
|
|
59
|
+
* @example PascalCase the final segment
|
|
60
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
61
|
+
*
|
|
62
|
+
* @example Suffix applied to the final segment only
|
|
63
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
71
64
|
*/
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const last = text[text.length - 1];
|
|
76
|
-
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
|
|
77
|
-
}
|
|
78
|
-
return text;
|
|
65
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
66
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
67
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
79
68
|
}
|
|
80
69
|
//#endregion
|
|
81
|
-
//#region ../../internals/utils/src/
|
|
70
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
82
71
|
/**
|
|
83
|
-
*
|
|
72
|
+
* JavaScript and Java reserved words.
|
|
73
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
74
|
+
*/
|
|
75
|
+
const reservedWords = new Set([
|
|
76
|
+
"abstract",
|
|
77
|
+
"arguments",
|
|
78
|
+
"boolean",
|
|
79
|
+
"break",
|
|
80
|
+
"byte",
|
|
81
|
+
"case",
|
|
82
|
+
"catch",
|
|
83
|
+
"char",
|
|
84
|
+
"class",
|
|
85
|
+
"const",
|
|
86
|
+
"continue",
|
|
87
|
+
"debugger",
|
|
88
|
+
"default",
|
|
89
|
+
"delete",
|
|
90
|
+
"do",
|
|
91
|
+
"double",
|
|
92
|
+
"else",
|
|
93
|
+
"enum",
|
|
94
|
+
"eval",
|
|
95
|
+
"export",
|
|
96
|
+
"extends",
|
|
97
|
+
"false",
|
|
98
|
+
"final",
|
|
99
|
+
"finally",
|
|
100
|
+
"float",
|
|
101
|
+
"for",
|
|
102
|
+
"function",
|
|
103
|
+
"goto",
|
|
104
|
+
"if",
|
|
105
|
+
"implements",
|
|
106
|
+
"import",
|
|
107
|
+
"in",
|
|
108
|
+
"instanceof",
|
|
109
|
+
"int",
|
|
110
|
+
"interface",
|
|
111
|
+
"let",
|
|
112
|
+
"long",
|
|
113
|
+
"native",
|
|
114
|
+
"new",
|
|
115
|
+
"null",
|
|
116
|
+
"package",
|
|
117
|
+
"private",
|
|
118
|
+
"protected",
|
|
119
|
+
"public",
|
|
120
|
+
"return",
|
|
121
|
+
"short",
|
|
122
|
+
"static",
|
|
123
|
+
"super",
|
|
124
|
+
"switch",
|
|
125
|
+
"synchronized",
|
|
126
|
+
"this",
|
|
127
|
+
"throw",
|
|
128
|
+
"throws",
|
|
129
|
+
"transient",
|
|
130
|
+
"true",
|
|
131
|
+
"try",
|
|
132
|
+
"typeof",
|
|
133
|
+
"var",
|
|
134
|
+
"void",
|
|
135
|
+
"volatile",
|
|
136
|
+
"while",
|
|
137
|
+
"with",
|
|
138
|
+
"yield",
|
|
139
|
+
"Array",
|
|
140
|
+
"Date",
|
|
141
|
+
"hasOwnProperty",
|
|
142
|
+
"Infinity",
|
|
143
|
+
"isFinite",
|
|
144
|
+
"isNaN",
|
|
145
|
+
"isPrototypeOf",
|
|
146
|
+
"length",
|
|
147
|
+
"Math",
|
|
148
|
+
"name",
|
|
149
|
+
"NaN",
|
|
150
|
+
"Number",
|
|
151
|
+
"Object",
|
|
152
|
+
"prototype",
|
|
153
|
+
"String",
|
|
154
|
+
"toString",
|
|
155
|
+
"undefined",
|
|
156
|
+
"valueOf"
|
|
157
|
+
]);
|
|
158
|
+
/**
|
|
159
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
84
160
|
*
|
|
85
161
|
* @example
|
|
86
|
-
*
|
|
87
|
-
*
|
|
162
|
+
* ```ts
|
|
163
|
+
* isValidVarName('status') // true
|
|
164
|
+
* isValidVarName('class') // false (reserved word)
|
|
165
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
166
|
+
* ```
|
|
88
167
|
*/
|
|
89
|
-
function
|
|
90
|
-
if (
|
|
91
|
-
return
|
|
168
|
+
function isValidVarName(name) {
|
|
169
|
+
if (!name || reservedWords.has(name)) return false;
|
|
170
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
92
171
|
}
|
|
93
172
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
173
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
174
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
175
|
+
*
|
|
176
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
177
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
178
|
+
* variable, type, or function names.
|
|
96
179
|
*
|
|
97
180
|
* @example
|
|
98
|
-
*
|
|
99
|
-
* // '
|
|
181
|
+
* ```ts
|
|
182
|
+
* ensureValidVarName('409') // '_409'
|
|
183
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
184
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
185
|
+
* ensureValidVarName('class') // '_class'
|
|
186
|
+
* ```
|
|
100
187
|
*/
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return `${key}: ${val}`;
|
|
105
|
-
}).filter(Boolean).join(",\n");
|
|
188
|
+
function ensureValidVarName(name) {
|
|
189
|
+
if (!name || isValidVarName(name)) return name;
|
|
190
|
+
return `_${name}`;
|
|
106
191
|
}
|
|
107
192
|
//#endregion
|
|
108
|
-
//#region ../../internals/
|
|
193
|
+
//#region ../../internals/shared/src/operation.ts
|
|
194
|
+
/**
|
|
195
|
+
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
196
|
+
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
197
|
+
*/
|
|
198
|
+
function getContentTypeSuffix(contentType) {
|
|
199
|
+
const baseType = contentType.split(";")[0].trim();
|
|
200
|
+
if (baseType === "application/json") return "Json";
|
|
201
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
202
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
203
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
204
|
+
if (parts.length === 0) return "Unknown";
|
|
205
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
206
|
+
}
|
|
109
207
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
|
|
208
|
+
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
209
|
+
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
210
|
+
*/
|
|
211
|
+
function getPerContentTypeName(baseName, suffix) {
|
|
212
|
+
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
213
|
+
return baseName + suffix;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Resolves per-content-type variant names for a set of content entries, deduplicating suffix
|
|
217
|
+
* collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
|
|
218
|
+
* the final (possibly counter-augmented) value, so callers can derive parallel names in another
|
|
219
|
+
* namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
|
|
220
|
+
*/
|
|
221
|
+
function resolveContentTypeVariants(entries, baseName) {
|
|
222
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
223
|
+
return entries.filter((entry) => entry.schema).map((entry) => {
|
|
224
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
225
|
+
let suffix = baseSuffix;
|
|
226
|
+
let name = getPerContentTypeName(baseName, suffix);
|
|
227
|
+
let counter = 2;
|
|
228
|
+
while (usedNames.has(name)) {
|
|
229
|
+
suffix = `${baseSuffix}${counter++}`;
|
|
230
|
+
name = getPerContentTypeName(baseName, suffix);
|
|
231
|
+
}
|
|
232
|
+
usedNames.add(name);
|
|
233
|
+
return {
|
|
234
|
+
name,
|
|
235
|
+
suffix,
|
|
236
|
+
schema: entry.schema,
|
|
237
|
+
keysToOmit: entry.keysToOmit,
|
|
238
|
+
contentType: entry.contentType
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region ../../internals/shared/src/group.ts
|
|
244
|
+
/**
|
|
245
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
246
|
+
* shared default naming so every plugin groups output consistently:
|
|
247
|
+
*
|
|
248
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
249
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
250
|
+
*
|
|
251
|
+
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
252
|
+
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
253
|
+
* per-plugin convention.
|
|
254
|
+
*
|
|
255
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
113
256
|
*
|
|
114
257
|
* @example
|
|
115
|
-
*
|
|
116
|
-
*
|
|
258
|
+
* ```ts
|
|
259
|
+
* createGroupConfig(group) // shared across every plugin
|
|
260
|
+
* ```
|
|
117
261
|
*/
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
262
|
+
function createGroupConfig(group) {
|
|
263
|
+
if (!group) return null;
|
|
264
|
+
const defaultName = (ctx) => {
|
|
265
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
266
|
+
return camelCase(ctx.group);
|
|
267
|
+
};
|
|
268
|
+
return {
|
|
269
|
+
...group,
|
|
270
|
+
name: group.name ? group.name : defaultName
|
|
271
|
+
};
|
|
127
272
|
}
|
|
128
273
|
//#endregion
|
|
129
274
|
//#region src/components/Operations.tsx
|
|
@@ -133,6 +278,7 @@ function Operations({ name, operations }) {
|
|
|
133
278
|
return prev;
|
|
134
279
|
}, {});
|
|
135
280
|
const pathsJSON = operations.reduce((prev, acc) => {
|
|
281
|
+
if (!ast.isHttpOperationNode(acc.node)) return prev;
|
|
136
282
|
prev[`"${acc.node.path}"`] = {
|
|
137
283
|
...prev[`"${acc.node.path}"`] ?? {},
|
|
138
284
|
[acc.node.method]: `operations["${acc.node.operationId}"]`
|
|
@@ -242,6 +388,61 @@ function shouldCoerce(coercion, type) {
|
|
|
242
388
|
return !!coercion[type];
|
|
243
389
|
}
|
|
244
390
|
/**
|
|
391
|
+
* Registered codecs, checked in order.
|
|
392
|
+
*/
|
|
393
|
+
const codecs = [{
|
|
394
|
+
matches(node) {
|
|
395
|
+
return node.type === "date" && node.representation === "date";
|
|
396
|
+
},
|
|
397
|
+
decode(node) {
|
|
398
|
+
return node.format === "date" ? "z.iso.date().transform((value) => new Date(value))" : "z.iso.datetime().transform((value) => new Date(value))";
|
|
399
|
+
},
|
|
400
|
+
encode(node) {
|
|
401
|
+
return node.format === "date" ? "z.date().transform((value) => value.toISOString().slice(0, 10))" : "z.date().transform((value) => value.toISOString())";
|
|
402
|
+
}
|
|
403
|
+
}];
|
|
404
|
+
/**
|
|
405
|
+
* Returns the codec for this node, or `undefined` when the node needs no
|
|
406
|
+
* encode/decode (its wire and runtime types match).
|
|
407
|
+
*/
|
|
408
|
+
function getCodec(node) {
|
|
409
|
+
if (!node) return void 0;
|
|
410
|
+
return codecs.find((codec) => codec.matches(node));
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Returns `true` when the node itself is encoded/decoded by a codec.
|
|
414
|
+
*/
|
|
415
|
+
function hasCodec(node) {
|
|
416
|
+
return getCodec(node) !== void 0;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Returns `true` when the schema transitively contains a codec node —
|
|
420
|
+
* a value whose runtime type differs from its wire type (see {@link hasCodec}),
|
|
421
|
+
* so it must be decoded (response) or encoded (request) at the validation boundary.
|
|
422
|
+
* `$ref`s are followed via their resolved schema; a `seen` set guards cycles.
|
|
423
|
+
*/
|
|
424
|
+
function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
|
|
425
|
+
if (!node) return false;
|
|
426
|
+
if (hasCodec(node)) return true;
|
|
427
|
+
if (node.type === "ref") {
|
|
428
|
+
if (!node.ref) return false;
|
|
429
|
+
const refName = extractRefName(node.ref);
|
|
430
|
+
if (refName) {
|
|
431
|
+
if (seen.has(refName)) return false;
|
|
432
|
+
seen.add(refName);
|
|
433
|
+
}
|
|
434
|
+
const resolved = ast.syncSchemaRef(node);
|
|
435
|
+
if (resolved.type === "ref") return false;
|
|
436
|
+
return containsCodec(resolved, seen);
|
|
437
|
+
}
|
|
438
|
+
const children = [];
|
|
439
|
+
if ("properties" in node && node.properties) children.push(...node.properties.map((prop) => prop.schema));
|
|
440
|
+
if ("items" in node && node.items) children.push(...node.items);
|
|
441
|
+
if ("members" in node && node.members) children.push(...node.members);
|
|
442
|
+
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
443
|
+
return children.some((child) => containsCodec(child, seen));
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
245
446
|
* Collects all resolved schema names for an operation's parameters and responses
|
|
246
447
|
* into a single lookup object, useful for building imports and type references.
|
|
247
448
|
*/
|
|
@@ -261,11 +462,11 @@ function buildSchemaNames(node, { params, resolver }) {
|
|
|
261
462
|
}
|
|
262
463
|
responses["default"] = resolver.resolveResponseName(node);
|
|
263
464
|
return {
|
|
264
|
-
request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) :
|
|
465
|
+
request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null,
|
|
265
466
|
parameters: {
|
|
266
|
-
path: pathParam ? resolver.resolvePathParamsName(node, pathParam) :
|
|
267
|
-
query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) :
|
|
268
|
-
header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) :
|
|
467
|
+
path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : null,
|
|
468
|
+
query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : null,
|
|
469
|
+
header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : null
|
|
269
470
|
},
|
|
270
471
|
responses,
|
|
271
472
|
errors
|
|
@@ -339,30 +540,44 @@ function lengthChecksMini({ min, max, pattern }) {
|
|
|
339
540
|
* to a schema value string using the chainable Zod v4 API.
|
|
340
541
|
*/
|
|
341
542
|
function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
543
|
+
const withModifier = (() => {
|
|
544
|
+
if (nullish || nullable && optional) return `${value}.nullish()`;
|
|
545
|
+
if (optional) return `${value}.optional()`;
|
|
546
|
+
if (nullable) return `${value}.nullable()`;
|
|
547
|
+
return value;
|
|
548
|
+
})();
|
|
549
|
+
const withDefault = defaultValue !== void 0 ? `${withModifier}.default(${formatDefault(defaultValue)})` : withModifier;
|
|
550
|
+
return description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
|
|
349
551
|
}
|
|
350
552
|
/**
|
|
351
553
|
* Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
|
|
352
554
|
* (`z.nullable()`, `z.optional()`, `z.nullish()`).
|
|
353
555
|
*/
|
|
354
556
|
function applyMiniModifiers({ value, nullable, optional, nullish, defaultValue }) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
if (defaultValue !== void 0) result = `z._default(${result}, ${formatDefault(defaultValue)})`;
|
|
362
|
-
return result;
|
|
557
|
+
const withModifier = (() => {
|
|
558
|
+
if (nullish) return `z.nullish(${value})`;
|
|
559
|
+
const withNullable = nullable ? `z.nullable(${value})` : value;
|
|
560
|
+
return optional ? `z.optional(${withNullable})` : withNullable;
|
|
561
|
+
})();
|
|
562
|
+
return defaultValue !== void 0 ? `z._default(${withModifier}, ${formatDefault(defaultValue)})` : withModifier;
|
|
363
563
|
}
|
|
364
564
|
//#endregion
|
|
365
565
|
//#region src/printers/printerZod.ts
|
|
566
|
+
function strictOneOfMember$1(member, node) {
|
|
567
|
+
if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
|
|
568
|
+
if (node.type === "ref") {
|
|
569
|
+
if (member.startsWith("z.lazy(")) return member;
|
|
570
|
+
const schema = ast.syncSchemaRef(node);
|
|
571
|
+
if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
|
|
572
|
+
}
|
|
573
|
+
return member;
|
|
574
|
+
}
|
|
575
|
+
__name(strictOneOfMember$1, "strictOneOfMember");
|
|
576
|
+
function getMemberConstraint(member) {
|
|
577
|
+
if (member.primitive === "string") return lengthConstraints(ast.narrowSchema(member, "string") ?? {}) || void 0;
|
|
578
|
+
if (member.primitive === "number" || member.primitive === "integer") return numberConstraints(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {}) || void 0;
|
|
579
|
+
if (member.primitive === "array") return lengthConstraints(ast.narrowSchema(member, "array") ?? {}) || void 0;
|
|
580
|
+
}
|
|
366
581
|
/**
|
|
367
582
|
* Zod v4 printer built with `definePrinter`.
|
|
368
583
|
*
|
|
@@ -399,8 +614,9 @@ const printerZod = ast.definePrinter((options) => {
|
|
|
399
614
|
return shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.bigint()" : "z.bigint()";
|
|
400
615
|
},
|
|
401
616
|
date(node) {
|
|
402
|
-
|
|
403
|
-
return
|
|
617
|
+
const codec = getCodec(node);
|
|
618
|
+
if (codec) return this.options.direction === "input" ? codec.encode(node) : codec.decode(node);
|
|
619
|
+
return "z.iso.date()";
|
|
404
620
|
},
|
|
405
621
|
datetime(node) {
|
|
406
622
|
const offset = node.offset || this.options.dateType === "stringOffset";
|
|
@@ -435,29 +651,30 @@ const printerZod = ast.definePrinter((options) => {
|
|
|
435
651
|
return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
|
|
436
652
|
},
|
|
437
653
|
ref(node) {
|
|
438
|
-
if (!node.name) return
|
|
439
|
-
const refName = node.ref ?
|
|
440
|
-
const
|
|
654
|
+
if (!node.name) return null;
|
|
655
|
+
const refName = node.ref ? extractRefName(node.ref) ?? node.name : node.name;
|
|
656
|
+
const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
|
|
657
|
+
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.resolveInputSchemaName(refName) ?? refName : this.options.resolver?.default(refName, "function") ?? refName : node.name;
|
|
441
658
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
|
|
442
659
|
return resolvedName;
|
|
443
660
|
},
|
|
444
661
|
object(node) {
|
|
445
|
-
|
|
662
|
+
const objectBase = `z.object(${buildObject(node.properties.map((prop) => {
|
|
446
663
|
const { name: propName, schema } = prop;
|
|
447
664
|
const meta = ast.syncSchemaRef(schema);
|
|
448
665
|
const isNullable = meta.nullable;
|
|
449
666
|
const isOptional = schema.optional;
|
|
450
667
|
const isNullish = schema.nullish;
|
|
451
668
|
const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
|
|
669
|
+
const savedCyclicSchemas = this.options.cyclicSchemas;
|
|
452
670
|
if (hasSelfRef) this.options.cyclicSchemas = void 0;
|
|
453
671
|
const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
|
|
454
|
-
if (hasSelfRef) this.options.cyclicSchemas =
|
|
672
|
+
if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
|
|
455
673
|
const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
|
|
456
674
|
output: baseOutput,
|
|
457
675
|
schema
|
|
458
676
|
}) || baseOutput : baseOutput;
|
|
459
|
-
|
|
460
|
-
if (schema.type !== "ref" && meta.type === "ref") descriptionToApply = void 0;
|
|
677
|
+
const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
|
|
461
678
|
const value = applyModifiers({
|
|
462
679
|
value: wrappedOutput,
|
|
463
680
|
nullable: isNullable,
|
|
@@ -466,78 +683,65 @@ const printerZod = ast.definePrinter((options) => {
|
|
|
466
683
|
defaultValue: meta.default,
|
|
467
684
|
description: descriptionToApply
|
|
468
685
|
});
|
|
469
|
-
if (hasSelfRef) return `get
|
|
470
|
-
return
|
|
471
|
-
})
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
686
|
+
if (hasSelfRef) return `get ${objectKey(propName)}() { return ${value} }`;
|
|
687
|
+
return `${objectKey(propName)}: ${value}`;
|
|
688
|
+
}))})`;
|
|
689
|
+
return (() => {
|
|
690
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
691
|
+
const catchallType = this.transform(node.additionalProperties);
|
|
692
|
+
return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
|
|
693
|
+
}
|
|
694
|
+
if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.createSchema({ type: "unknown" }))})`;
|
|
695
|
+
if (node.additionalProperties === false) return `${objectBase}.strict()`;
|
|
696
|
+
return objectBase;
|
|
697
|
+
})();
|
|
478
698
|
},
|
|
479
699
|
array(node) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return result;
|
|
700
|
+
const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
|
|
701
|
+
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
|
|
483
702
|
},
|
|
484
703
|
tuple(node) {
|
|
485
|
-
return `z.tuple(
|
|
704
|
+
return `z.tuple(${buildList((node.items ?? []).map((item) => this.transform(item)).filter(Boolean))})`;
|
|
486
705
|
},
|
|
487
706
|
union(node) {
|
|
488
707
|
const nodeMembers = node.members ?? [];
|
|
489
|
-
const members = nodeMembers.map((
|
|
708
|
+
const members = nodeMembers.map((memberNode) => {
|
|
709
|
+
const member = this.transform(memberNode);
|
|
710
|
+
return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
|
|
711
|
+
}).filter(Boolean);
|
|
490
712
|
if (members.length === 0) return "";
|
|
491
713
|
if (members.length === 1) return members[0];
|
|
492
|
-
if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)},
|
|
493
|
-
return `z.union(
|
|
714
|
+
if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
715
|
+
return `z.union(${buildList(members)})`;
|
|
494
716
|
},
|
|
495
717
|
intersection(node) {
|
|
496
718
|
const members = node.members ?? [];
|
|
497
719
|
if (members.length === 0) return "";
|
|
498
720
|
const [first, ...rest] = members;
|
|
499
721
|
if (!first) return "";
|
|
500
|
-
|
|
501
|
-
if (!
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if (c) {
|
|
506
|
-
base += c;
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
} else if (member.primitive === "number" || member.primitive === "integer") {
|
|
510
|
-
const c = numberConstraints(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {});
|
|
511
|
-
if (c) {
|
|
512
|
-
base += c;
|
|
513
|
-
continue;
|
|
514
|
-
}
|
|
515
|
-
} else if (member.primitive === "array") {
|
|
516
|
-
const c = lengthConstraints(ast.narrowSchema(member, "array") ?? {});
|
|
517
|
-
if (c) {
|
|
518
|
-
base += c;
|
|
519
|
-
continue;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
722
|
+
const firstBase = this.transform(first);
|
|
723
|
+
if (!firstBase) return "";
|
|
724
|
+
return rest.reduce((acc, member) => {
|
|
725
|
+
const constraint = getMemberConstraint(member);
|
|
726
|
+
if (constraint) return acc + constraint;
|
|
522
727
|
const transformed = this.transform(member);
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
return base;
|
|
728
|
+
return transformed ? `${acc}.and(${transformed})` : acc;
|
|
729
|
+
}, firstBase);
|
|
526
730
|
},
|
|
527
731
|
...options.nodes
|
|
528
732
|
},
|
|
529
733
|
print(node) {
|
|
530
734
|
const { keysToOmit } = this.options;
|
|
531
|
-
|
|
532
|
-
if (!
|
|
735
|
+
const transformed = this.transform(node);
|
|
736
|
+
if (!transformed) return null;
|
|
533
737
|
const meta = ast.syncSchemaRef(node);
|
|
534
|
-
if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
|
|
535
|
-
const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
536
|
-
if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
|
|
537
|
-
else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
|
|
538
|
-
}
|
|
539
738
|
return applyModifiers({
|
|
540
|
-
value:
|
|
739
|
+
value: (() => {
|
|
740
|
+
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
741
|
+
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
742
|
+
if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
|
|
743
|
+
return `${transformed}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
|
|
744
|
+
})(),
|
|
541
745
|
nullable: meta.nullable,
|
|
542
746
|
optional: meta.optional,
|
|
543
747
|
nullish: meta.nullish,
|
|
@@ -549,6 +753,15 @@ const printerZod = ast.definePrinter((options) => {
|
|
|
549
753
|
});
|
|
550
754
|
//#endregion
|
|
551
755
|
//#region src/printers/printerZodMini.ts
|
|
756
|
+
function strictOneOfMember(member, node) {
|
|
757
|
+
if (node.type === "object" && (node.additionalProperties === void 0 || node.additionalProperties === false)) return member.replace(/^z\.object\(/, "z.strictObject(");
|
|
758
|
+
return member;
|
|
759
|
+
}
|
|
760
|
+
function getMemberConstraintMini(member) {
|
|
761
|
+
if (member.primitive === "string") return lengthChecksMini(ast.narrowSchema(member, "string") ?? {}) || void 0;
|
|
762
|
+
if (member.primitive === "number" || member.primitive === "integer") return numberChecksMini(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {}) || void 0;
|
|
763
|
+
if (member.primitive === "array") return lengthChecksMini(ast.narrowSchema(member, "array") ?? {}) || void 0;
|
|
764
|
+
}
|
|
552
765
|
/**
|
|
553
766
|
* Zod v4 **Mini** printer built with `definePrinter`.
|
|
554
767
|
*
|
|
@@ -617,23 +830,24 @@ const printerZodMini = ast.definePrinter((options) => {
|
|
|
617
830
|
return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
|
|
618
831
|
},
|
|
619
832
|
ref(node) {
|
|
620
|
-
if (!node.name) return
|
|
621
|
-
const refName = node.ref ?
|
|
833
|
+
if (!node.name) return null;
|
|
834
|
+
const refName = node.ref ? extractRefName(node.ref) ?? node.name : node.name;
|
|
622
835
|
const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
|
|
623
836
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
|
|
624
837
|
return resolvedName;
|
|
625
838
|
},
|
|
626
839
|
object(node) {
|
|
627
|
-
return `z.object(
|
|
840
|
+
return `z.object(${buildObject(node.properties.map((prop) => {
|
|
628
841
|
const { name: propName, schema } = prop;
|
|
629
842
|
const meta = ast.syncSchemaRef(schema);
|
|
630
843
|
const isNullable = meta.nullable;
|
|
631
844
|
const isOptional = schema.optional;
|
|
632
845
|
const isNullish = schema.nullish;
|
|
633
846
|
const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
|
|
847
|
+
const savedCyclicSchemas = this.options.cyclicSchemas;
|
|
634
848
|
if (hasSelfRef) this.options.cyclicSchemas = void 0;
|
|
635
849
|
const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
|
|
636
|
-
if (hasSelfRef) this.options.cyclicSchemas =
|
|
850
|
+
if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
|
|
637
851
|
const value = applyMiniModifiers({
|
|
638
852
|
value: this.options.wrapOutput ? this.options.wrapOutput({
|
|
639
853
|
output: baseOutput,
|
|
@@ -644,72 +858,56 @@ const printerZodMini = ast.definePrinter((options) => {
|
|
|
644
858
|
nullish: isNullish,
|
|
645
859
|
defaultValue: meta.default
|
|
646
860
|
});
|
|
647
|
-
if (hasSelfRef) return `get
|
|
648
|
-
return
|
|
649
|
-
})
|
|
861
|
+
if (hasSelfRef) return `get ${objectKey(propName)}() { return ${value} }`;
|
|
862
|
+
return `${objectKey(propName)}: ${value}`;
|
|
863
|
+
}))})`;
|
|
650
864
|
},
|
|
651
865
|
array(node) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
return result;
|
|
866
|
+
const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
|
|
867
|
+
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
|
|
655
868
|
},
|
|
656
869
|
tuple(node) {
|
|
657
|
-
return `z.tuple(
|
|
870
|
+
return `z.tuple(${buildList((node.items ?? []).map((item) => this.transform(item)).filter(Boolean))})`;
|
|
658
871
|
},
|
|
659
872
|
union(node) {
|
|
660
873
|
const nodeMembers = node.members ?? [];
|
|
661
|
-
const members = nodeMembers.map((
|
|
874
|
+
const members = nodeMembers.map((memberNode) => {
|
|
875
|
+
const member = this.transform(memberNode);
|
|
876
|
+
return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
|
|
877
|
+
}).filter(Boolean);
|
|
662
878
|
if (members.length === 0) return "";
|
|
663
879
|
if (members.length === 1) return members[0];
|
|
664
|
-
if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)},
|
|
665
|
-
return `z.union(
|
|
880
|
+
if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
881
|
+
return `z.union(${buildList(members)})`;
|
|
666
882
|
},
|
|
667
883
|
intersection(node) {
|
|
668
884
|
const members = node.members ?? [];
|
|
669
885
|
if (members.length === 0) return "";
|
|
670
886
|
const [first, ...rest] = members;
|
|
671
887
|
if (!first) return "";
|
|
672
|
-
|
|
673
|
-
if (!
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (c) {
|
|
678
|
-
base += c;
|
|
679
|
-
continue;
|
|
680
|
-
}
|
|
681
|
-
} else if (member.primitive === "number" || member.primitive === "integer") {
|
|
682
|
-
const c = numberChecksMini(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {});
|
|
683
|
-
if (c) {
|
|
684
|
-
base += c;
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
} else if (member.primitive === "array") {
|
|
688
|
-
const c = lengthChecksMini(ast.narrowSchema(member, "array") ?? {});
|
|
689
|
-
if (c) {
|
|
690
|
-
base += c;
|
|
691
|
-
continue;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
888
|
+
const firstBase = this.transform(first);
|
|
889
|
+
if (!firstBase) return "";
|
|
890
|
+
return rest.reduce((acc, member) => {
|
|
891
|
+
const constraint = getMemberConstraintMini(member);
|
|
892
|
+
if (constraint) return acc + constraint;
|
|
694
893
|
const transformed = this.transform(member);
|
|
695
|
-
|
|
696
|
-
}
|
|
697
|
-
return base;
|
|
894
|
+
return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
|
|
895
|
+
}, firstBase);
|
|
698
896
|
},
|
|
699
897
|
...options.nodes
|
|
700
898
|
},
|
|
701
899
|
print(node) {
|
|
702
900
|
const { keysToOmit } = this.options;
|
|
703
|
-
|
|
704
|
-
if (!
|
|
901
|
+
const transformed = this.transform(node);
|
|
902
|
+
if (!transformed) return null;
|
|
705
903
|
const meta = ast.syncSchemaRef(node);
|
|
706
|
-
if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
|
|
707
|
-
const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
708
|
-
if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
|
|
709
|
-
else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
|
|
710
|
-
}
|
|
711
904
|
return applyMiniModifiers({
|
|
712
|
-
value:
|
|
905
|
+
value: (() => {
|
|
906
|
+
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
907
|
+
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
908
|
+
if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
|
|
909
|
+
return `${transformed}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
|
|
910
|
+
})(),
|
|
713
911
|
nullable: meta.nullable,
|
|
714
912
|
optional: meta.optional,
|
|
715
913
|
nullish: meta.nullish,
|
|
@@ -720,6 +918,62 @@ const printerZodMini = ast.definePrinter((options) => {
|
|
|
720
918
|
});
|
|
721
919
|
//#endregion
|
|
722
920
|
//#region src/generators/zodGenerator.tsx
|
|
921
|
+
const zodPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
922
|
+
const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
923
|
+
/**
|
|
924
|
+
* Returns the cached `output`/`input` direction printers for a resolver, building them on
|
|
925
|
+
* first use. The `input` printer encodes `Date → string` for request bodies; `output` decodes
|
|
926
|
+
* `string → Date` for responses. Schemas without `dateType: 'date'` fields print identically.
|
|
927
|
+
*/
|
|
928
|
+
function getStdPrinters(resolver, params) {
|
|
929
|
+
const cached = zodPrinterCache.get(resolver);
|
|
930
|
+
if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.dateType === params.dateType) return {
|
|
931
|
+
output: cached.output,
|
|
932
|
+
input: cached.input
|
|
933
|
+
};
|
|
934
|
+
const base = {
|
|
935
|
+
...params,
|
|
936
|
+
resolver
|
|
937
|
+
};
|
|
938
|
+
const output = printerZod({
|
|
939
|
+
...base,
|
|
940
|
+
direction: "output"
|
|
941
|
+
});
|
|
942
|
+
const input = printerZod({
|
|
943
|
+
...base,
|
|
944
|
+
direction: "input"
|
|
945
|
+
});
|
|
946
|
+
zodPrinterCache.set(resolver, {
|
|
947
|
+
output,
|
|
948
|
+
input,
|
|
949
|
+
coercion: params.coercion,
|
|
950
|
+
guidType: params.guidType,
|
|
951
|
+
dateType: params.dateType
|
|
952
|
+
});
|
|
953
|
+
return {
|
|
954
|
+
output,
|
|
955
|
+
input
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
function getMiniPrinter(resolver, params) {
|
|
959
|
+
const cached = zodMiniPrinterCache.get(resolver);
|
|
960
|
+
if (cached && cached.guidType === params.guidType) return cached.printer;
|
|
961
|
+
const p = printerZodMini({
|
|
962
|
+
...params,
|
|
963
|
+
resolver
|
|
964
|
+
});
|
|
965
|
+
zodMiniPrinterCache.set(resolver, {
|
|
966
|
+
printer: p,
|
|
967
|
+
guidType: params.guidType
|
|
968
|
+
});
|
|
969
|
+
return p;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Built-in generator for `@kubb/plugin-zod`. Emits one Zod schema per
|
|
973
|
+
* schema in the spec plus per-operation request/response/parameter schemas.
|
|
974
|
+
* When `mini: true`, schemas use the Zod Mini functional API instead of
|
|
975
|
+
* chainable methods.
|
|
976
|
+
*/
|
|
723
977
|
const zodGenerator = defineGenerator({
|
|
724
978
|
name: "zod",
|
|
725
979
|
renderer: jsxRenderer,
|
|
@@ -728,9 +982,11 @@ const zodGenerator = defineGenerator({
|
|
|
728
982
|
const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
|
|
729
983
|
const dateType = adapter.options.dateType;
|
|
730
984
|
if (!node.name) return;
|
|
731
|
-
const mode = ctx.getMode(output);
|
|
732
985
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
733
|
-
const
|
|
986
|
+
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
987
|
+
const hasCodec = !mini && containsCodec(node);
|
|
988
|
+
const codecRefNames = new Set(hasCodec ? ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 }) : []);
|
|
989
|
+
const importEntries = adapter.getImports(node, (schemaName) => ({
|
|
734
990
|
name: resolver.resolveSchemaName(schemaName),
|
|
735
991
|
path: resolver.resolveFile({
|
|
736
992
|
name: schemaName,
|
|
@@ -738,9 +994,27 @@ const zodGenerator = defineGenerator({
|
|
|
738
994
|
}, {
|
|
739
995
|
root,
|
|
740
996
|
output,
|
|
741
|
-
group
|
|
997
|
+
group: group ?? void 0
|
|
742
998
|
}).path
|
|
743
999
|
}));
|
|
1000
|
+
const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
|
|
1001
|
+
name: [resolver.resolveInputSchemaName(schemaName)],
|
|
1002
|
+
path: resolver.resolveFile({
|
|
1003
|
+
name: schemaName,
|
|
1004
|
+
extname: ".ts"
|
|
1005
|
+
}, {
|
|
1006
|
+
root,
|
|
1007
|
+
output,
|
|
1008
|
+
group: group ?? void 0
|
|
1009
|
+
}).path
|
|
1010
|
+
})) : [];
|
|
1011
|
+
const seenImports = /* @__PURE__ */ new Set();
|
|
1012
|
+
const imports = [...importEntries, ...inputImportEntries].filter((imp) => {
|
|
1013
|
+
const key = `${Array.isArray(imp.name) ? imp.name.join(",") : imp.name}|${imp.path}`;
|
|
1014
|
+
if (seenImports.has(key)) return false;
|
|
1015
|
+
seenImports.add(key);
|
|
1016
|
+
return true;
|
|
1017
|
+
});
|
|
744
1018
|
const meta = {
|
|
745
1019
|
name: resolver.resolveSchemaName(node.name),
|
|
746
1020
|
file: resolver.resolveFile({
|
|
@@ -749,37 +1023,43 @@ const zodGenerator = defineGenerator({
|
|
|
749
1023
|
}, {
|
|
750
1024
|
root,
|
|
751
1025
|
output,
|
|
752
|
-
group
|
|
1026
|
+
group: group ?? void 0
|
|
753
1027
|
})
|
|
754
1028
|
};
|
|
755
|
-
const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) :
|
|
756
|
-
const
|
|
757
|
-
|
|
1029
|
+
const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : null;
|
|
1030
|
+
const stdPrinters = mini ? null : getStdPrinters(resolver, {
|
|
1031
|
+
coercion,
|
|
758
1032
|
guidType,
|
|
1033
|
+
dateType,
|
|
759
1034
|
wrapOutput,
|
|
760
|
-
resolver,
|
|
761
1035
|
cyclicSchemas,
|
|
762
1036
|
nodes: printer?.nodes
|
|
763
|
-
})
|
|
764
|
-
|
|
1037
|
+
});
|
|
1038
|
+
const schemaPrinter = mini ? getMiniPrinter(resolver, {
|
|
765
1039
|
guidType,
|
|
766
|
-
dateType,
|
|
767
1040
|
wrapOutput,
|
|
768
|
-
resolver,
|
|
769
1041
|
cyclicSchemas,
|
|
770
1042
|
nodes: printer?.nodes
|
|
771
|
-
});
|
|
1043
|
+
}) : stdPrinters.output;
|
|
772
1044
|
return /* @__PURE__ */ jsxs(File, {
|
|
773
1045
|
baseName: meta.file.baseName,
|
|
774
1046
|
path: meta.file.path,
|
|
775
1047
|
meta: meta.file.meta,
|
|
776
|
-
banner: resolver.resolveBanner(
|
|
1048
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
777
1049
|
output,
|
|
778
|
-
config
|
|
1050
|
+
config,
|
|
1051
|
+
file: {
|
|
1052
|
+
path: meta.file.path,
|
|
1053
|
+
baseName: meta.file.baseName
|
|
1054
|
+
}
|
|
779
1055
|
}),
|
|
780
|
-
footer: resolver.resolveFooter(
|
|
1056
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
781
1057
|
output,
|
|
782
|
-
config
|
|
1058
|
+
config,
|
|
1059
|
+
file: {
|
|
1060
|
+
path: meta.file.path,
|
|
1061
|
+
baseName: meta.file.baseName
|
|
1062
|
+
}
|
|
783
1063
|
}),
|
|
784
1064
|
children: [
|
|
785
1065
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -787,25 +1067,35 @@ const zodGenerator = defineGenerator({
|
|
|
787
1067
|
path: importPath,
|
|
788
1068
|
isNameSpace: isZodImport
|
|
789
1069
|
}),
|
|
790
|
-
|
|
1070
|
+
imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
|
|
791
1071
|
root: meta.file.path,
|
|
792
1072
|
path: imp.path,
|
|
793
1073
|
name: imp.name
|
|
794
|
-
}, [
|
|
1074
|
+
}, [
|
|
1075
|
+
node.name,
|
|
1076
|
+
imp.path,
|
|
1077
|
+
imp.name
|
|
1078
|
+
].join("-"))),
|
|
795
1079
|
/* @__PURE__ */ jsx(Zod, {
|
|
796
1080
|
name: meta.name,
|
|
797
1081
|
node,
|
|
798
1082
|
printer: schemaPrinter,
|
|
799
1083
|
inferTypeName
|
|
1084
|
+
}),
|
|
1085
|
+
hasCodec && stdPrinters && /* @__PURE__ */ jsx(Zod, {
|
|
1086
|
+
name: resolver.resolveInputSchemaName(node.name),
|
|
1087
|
+
node,
|
|
1088
|
+
printer: stdPrinters.input,
|
|
1089
|
+
inferTypeName: inferred ? resolver.resolveInputSchemaTypeName(node.name) : null
|
|
800
1090
|
})
|
|
801
1091
|
]
|
|
802
1092
|
});
|
|
803
1093
|
},
|
|
804
1094
|
operation(node, ctx) {
|
|
1095
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
805
1096
|
const { adapter, config, resolver, root } = ctx;
|
|
806
1097
|
const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
|
|
807
1098
|
const dateType = adapter.options.dateType;
|
|
808
|
-
const mode = ctx.getMode(output);
|
|
809
1099
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
810
1100
|
const params = ast.caseParams(node.parameters, paramsCasing);
|
|
811
1101
|
const meta = { file: resolver.resolveFile({
|
|
@@ -816,31 +1106,37 @@ const zodGenerator = defineGenerator({
|
|
|
816
1106
|
}, {
|
|
817
1107
|
root,
|
|
818
1108
|
output,
|
|
819
|
-
group
|
|
1109
|
+
group: group ?? void 0
|
|
820
1110
|
}) };
|
|
821
|
-
const cyclicSchemas =
|
|
822
|
-
function renderSchemaEntry({ schema, name, keysToOmit }) {
|
|
1111
|
+
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
1112
|
+
function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
|
|
823
1113
|
if (!schema) return null;
|
|
824
|
-
const inferTypeName = inferred ? resolver.resolveTypeName(name) :
|
|
1114
|
+
const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
|
|
1115
|
+
const codecRefNames = direction === "input" && !mini ? new Set(ast.collect(schema, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 })) : null;
|
|
825
1116
|
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
826
|
-
name: resolver.resolveSchemaName(schemaName),
|
|
1117
|
+
name: codecRefNames?.has(schemaName) ? resolver.resolveInputSchemaName(schemaName) : resolver.resolveSchemaName(schemaName),
|
|
827
1118
|
path: resolver.resolveFile({
|
|
828
1119
|
name: schemaName,
|
|
829
1120
|
extname: ".ts"
|
|
830
1121
|
}, {
|
|
831
1122
|
root,
|
|
832
1123
|
output,
|
|
833
|
-
group
|
|
1124
|
+
group: group ?? void 0
|
|
834
1125
|
}).path
|
|
835
1126
|
}));
|
|
836
|
-
const schemaPrinter = mini ? printerZodMini({
|
|
1127
|
+
const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
|
|
837
1128
|
guidType,
|
|
838
1129
|
wrapOutput,
|
|
839
1130
|
resolver,
|
|
840
1131
|
keysToOmit,
|
|
841
1132
|
cyclicSchemas,
|
|
842
1133
|
nodes: printer?.nodes
|
|
843
|
-
}) :
|
|
1134
|
+
}) : getMiniPrinter(resolver, {
|
|
1135
|
+
guidType,
|
|
1136
|
+
wrapOutput,
|
|
1137
|
+
cyclicSchemas,
|
|
1138
|
+
nodes: printer?.nodes
|
|
1139
|
+
}) : keysToOmit?.length ? printerZod({
|
|
844
1140
|
coercion,
|
|
845
1141
|
guidType,
|
|
846
1142
|
dateType,
|
|
@@ -848,9 +1144,17 @@ const zodGenerator = defineGenerator({
|
|
|
848
1144
|
resolver,
|
|
849
1145
|
keysToOmit,
|
|
850
1146
|
cyclicSchemas,
|
|
1147
|
+
nodes: printer?.nodes,
|
|
1148
|
+
direction
|
|
1149
|
+
}) : getStdPrinters(resolver, {
|
|
1150
|
+
coercion,
|
|
1151
|
+
guidType,
|
|
1152
|
+
dateType,
|
|
1153
|
+
wrapOutput,
|
|
1154
|
+
cyclicSchemas,
|
|
851
1155
|
nodes: printer?.nodes
|
|
852
|
-
});
|
|
853
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1156
|
+
})[direction];
|
|
1157
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
|
|
854
1158
|
root: meta.file.path,
|
|
855
1159
|
path: imp.path,
|
|
856
1160
|
name: imp.name
|
|
@@ -865,22 +1169,48 @@ const zodGenerator = defineGenerator({
|
|
|
865
1169
|
inferTypeName
|
|
866
1170
|
})] });
|
|
867
1171
|
}
|
|
1172
|
+
function buildContentTypeVariants(entries, baseName, decorate, direction) {
|
|
1173
|
+
const variants = resolveContentTypeVariants(entries, baseName);
|
|
1174
|
+
const unionSchema = ast.createSchema({
|
|
1175
|
+
type: "union",
|
|
1176
|
+
members: variants.map((variant) => ast.createSchema({
|
|
1177
|
+
type: "ref",
|
|
1178
|
+
name: variant.name
|
|
1179
|
+
}))
|
|
1180
|
+
});
|
|
1181
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [variants.map((variant) => renderSchemaEntry({
|
|
1182
|
+
schema: decorate ? decorate(variant.schema) : variant.schema,
|
|
1183
|
+
name: variant.name,
|
|
1184
|
+
keysToOmit: variant.keysToOmit,
|
|
1185
|
+
direction
|
|
1186
|
+
})), renderSchemaEntry({
|
|
1187
|
+
schema: unionSchema,
|
|
1188
|
+
name: baseName,
|
|
1189
|
+
direction
|
|
1190
|
+
})] });
|
|
1191
|
+
}
|
|
868
1192
|
const paramSchemas = params.map((param) => renderSchemaEntry({
|
|
869
1193
|
schema: param.schema,
|
|
870
|
-
name: resolver.resolveParamName(node, param)
|
|
1194
|
+
name: resolver.resolveParamName(node, param),
|
|
1195
|
+
direction: "input"
|
|
871
1196
|
}));
|
|
872
|
-
const responseSchemas = node.responses.map((res) =>
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1197
|
+
const responseSchemas = node.responses.map((res) => {
|
|
1198
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1199
|
+
if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.resolveResponseStatusName(node, res.statusCode));
|
|
1200
|
+
const primary = variants[0] ?? res.content?.[0];
|
|
1201
|
+
return renderSchemaEntry({
|
|
1202
|
+
schema: primary?.schema ?? null,
|
|
1203
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
1204
|
+
keysToOmit: primary?.keysToOmit
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
878
1208
|
const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
|
|
879
1209
|
const responseUnionName = resolver.resolveResponseName(node);
|
|
880
|
-
if (new Set(responsesWithSchema.flatMap((res) => res.schema ? adapter.getImports(
|
|
1210
|
+
if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
|
|
881
1211
|
name: resolver.resolveSchemaName(schemaName),
|
|
882
1212
|
path: ""
|
|
883
|
-
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseUnionName)) return null;
|
|
1213
|
+
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
|
|
884
1214
|
const members = responsesWithSchema.map((res) => ast.createSchema({
|
|
885
1215
|
type: "ref",
|
|
886
1216
|
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
@@ -893,25 +1223,46 @@ const zodGenerator = defineGenerator({
|
|
|
893
1223
|
name: responseUnionName
|
|
894
1224
|
});
|
|
895
1225
|
})() : null;
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1226
|
+
const requestBodyContent = node.requestBody?.content ?? [];
|
|
1227
|
+
const requestSchema = (() => {
|
|
1228
|
+
if (requestBodyContent.length === 0) return null;
|
|
1229
|
+
if (requestBodyContent.length === 1) {
|
|
1230
|
+
const entry = requestBodyContent[0];
|
|
1231
|
+
if (!entry.schema) return null;
|
|
1232
|
+
return renderSchemaEntry({
|
|
1233
|
+
schema: {
|
|
1234
|
+
...entry.schema,
|
|
1235
|
+
description: node.requestBody.description ?? entry.schema.description
|
|
1236
|
+
},
|
|
1237
|
+
name: resolver.resolveDataName(node),
|
|
1238
|
+
keysToOmit: entry.keysToOmit,
|
|
1239
|
+
direction: "input"
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
return buildContentTypeVariants(requestBodyContent, resolver.resolveDataName(node), (schema) => ({
|
|
1243
|
+
...schema,
|
|
1244
|
+
description: node.requestBody.description ?? schema.description
|
|
1245
|
+
}), "input");
|
|
1246
|
+
})();
|
|
904
1247
|
return /* @__PURE__ */ jsxs(File, {
|
|
905
1248
|
baseName: meta.file.baseName,
|
|
906
1249
|
path: meta.file.path,
|
|
907
1250
|
meta: meta.file.meta,
|
|
908
|
-
banner: resolver.resolveBanner(
|
|
1251
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
909
1252
|
output,
|
|
910
|
-
config
|
|
1253
|
+
config,
|
|
1254
|
+
file: {
|
|
1255
|
+
path: meta.file.path,
|
|
1256
|
+
baseName: meta.file.baseName
|
|
1257
|
+
}
|
|
911
1258
|
}),
|
|
912
|
-
footer: resolver.resolveFooter(
|
|
1259
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
913
1260
|
output,
|
|
914
|
-
config
|
|
1261
|
+
config,
|
|
1262
|
+
file: {
|
|
1263
|
+
path: meta.file.path,
|
|
1264
|
+
baseName: meta.file.baseName
|
|
1265
|
+
}
|
|
915
1266
|
}),
|
|
916
1267
|
children: [
|
|
917
1268
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -927,7 +1278,7 @@ const zodGenerator = defineGenerator({
|
|
|
927
1278
|
});
|
|
928
1279
|
},
|
|
929
1280
|
operations(nodes, ctx) {
|
|
930
|
-
const {
|
|
1281
|
+
const { config, resolver, root } = ctx;
|
|
931
1282
|
const { output, importPath, group, operations, paramsCasing } = ctx.options;
|
|
932
1283
|
if (!operations) return;
|
|
933
1284
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
@@ -937,9 +1288,9 @@ const zodGenerator = defineGenerator({
|
|
|
937
1288
|
}, {
|
|
938
1289
|
root,
|
|
939
1290
|
output,
|
|
940
|
-
group
|
|
1291
|
+
group: group ?? void 0
|
|
941
1292
|
}) };
|
|
942
|
-
const transformedOperations = nodes.map((node) => {
|
|
1293
|
+
const transformedOperations = nodes.filter(ast.isHttpOperationNode).map((node) => {
|
|
943
1294
|
return {
|
|
944
1295
|
node,
|
|
945
1296
|
data: buildSchemaNames(node, {
|
|
@@ -962,7 +1313,7 @@ const zodGenerator = defineGenerator({
|
|
|
962
1313
|
}, {
|
|
963
1314
|
root,
|
|
964
1315
|
output,
|
|
965
|
-
group
|
|
1316
|
+
group: group ?? void 0
|
|
966
1317
|
});
|
|
967
1318
|
return names.map((name) => /* @__PURE__ */ jsx(File.Import, {
|
|
968
1319
|
name: [name],
|
|
@@ -974,13 +1325,21 @@ const zodGenerator = defineGenerator({
|
|
|
974
1325
|
baseName: meta.file.baseName,
|
|
975
1326
|
path: meta.file.path,
|
|
976
1327
|
meta: meta.file.meta,
|
|
977
|
-
banner: resolver.resolveBanner(
|
|
1328
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
978
1329
|
output,
|
|
979
|
-
config
|
|
1330
|
+
config,
|
|
1331
|
+
file: {
|
|
1332
|
+
path: meta.file.path,
|
|
1333
|
+
baseName: meta.file.baseName
|
|
1334
|
+
}
|
|
980
1335
|
}),
|
|
981
|
-
footer: resolver.resolveFooter(
|
|
1336
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
982
1337
|
output,
|
|
983
|
-
config
|
|
1338
|
+
config,
|
|
1339
|
+
file: {
|
|
1340
|
+
path: meta.file.path,
|
|
1341
|
+
baseName: meta.file.baseName
|
|
1342
|
+
}
|
|
984
1343
|
}),
|
|
985
1344
|
children: [
|
|
986
1345
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1001,92 +1360,110 @@ const zodGenerator = defineGenerator({
|
|
|
1001
1360
|
//#endregion
|
|
1002
1361
|
//#region src/resolvers/resolverZod.ts
|
|
1003
1362
|
/**
|
|
1004
|
-
*
|
|
1363
|
+
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
1364
|
+
* paths for every generated Zod schema. Schemas use camelCase with a
|
|
1365
|
+
* `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase
|
|
1366
|
+
* with a `SchemaType` suffix (`PetSchemaType`), so the value and the type
|
|
1367
|
+
* never share an identifier even when the schema name is all-uppercase.
|
|
1005
1368
|
*
|
|
1006
|
-
*
|
|
1369
|
+
* @example Resolve schema and type names
|
|
1370
|
+
* ```ts
|
|
1371
|
+
* import { resolverZod } from '@kubb/plugin-zod'
|
|
1007
1372
|
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1373
|
+
* resolverZod.default('list pets', 'function') // 'listPetsSchema'
|
|
1374
|
+
* resolverZod.resolveSchemaTypeName('pet') // 'PetSchemaType'
|
|
1375
|
+
* ```
|
|
1010
1376
|
*/
|
|
1011
|
-
const resolverZod = defineResolver((
|
|
1377
|
+
const resolverZod = defineResolver(() => {
|
|
1012
1378
|
return {
|
|
1013
1379
|
name: "default",
|
|
1014
1380
|
pluginName: "plugin-zod",
|
|
1015
1381
|
default(name, type) {
|
|
1016
|
-
return
|
|
1017
|
-
|
|
1018
|
-
suffix: type ? "schema" : void 0
|
|
1019
|
-
});
|
|
1382
|
+
if (type === "file") return toFilePath(name, (part) => camelCase(part, { suffix: "schema" }));
|
|
1383
|
+
return ensureValidVarName(camelCase(name, { suffix: type ? "schema" : void 0 }));
|
|
1020
1384
|
},
|
|
1021
1385
|
resolveSchemaName(name) {
|
|
1022
|
-
return camelCase(name, { suffix: "schema" });
|
|
1386
|
+
return ensureValidVarName(camelCase(name, { suffix: "schema" }));
|
|
1023
1387
|
},
|
|
1024
1388
|
resolveSchemaTypeName(name) {
|
|
1025
|
-
return pascalCase(name, { suffix: "schema" });
|
|
1389
|
+
return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
|
|
1390
|
+
},
|
|
1391
|
+
resolveInputSchemaName(name) {
|
|
1392
|
+
return this.resolveSchemaName(`${name} input`);
|
|
1393
|
+
},
|
|
1394
|
+
resolveInputSchemaTypeName(name) {
|
|
1395
|
+
return this.resolveSchemaTypeName(`${name} input`);
|
|
1026
1396
|
},
|
|
1027
1397
|
resolveTypeName(name) {
|
|
1028
|
-
return pascalCase(name);
|
|
1398
|
+
return ensureValidVarName(pascalCase(name, { suffix: "type" }));
|
|
1029
1399
|
},
|
|
1030
1400
|
resolvePathName(name, type) {
|
|
1031
|
-
return
|
|
1401
|
+
return this.default(name, type);
|
|
1032
1402
|
},
|
|
1033
1403
|
resolveParamName(node, param) {
|
|
1034
|
-
return
|
|
1404
|
+
return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
|
|
1035
1405
|
},
|
|
1036
1406
|
resolveResponseStatusName(node, statusCode) {
|
|
1037
|
-
return
|
|
1407
|
+
return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`);
|
|
1038
1408
|
},
|
|
1039
1409
|
resolveDataName(node) {
|
|
1040
|
-
return
|
|
1410
|
+
return this.resolveSchemaName(`${node.operationId} Data`);
|
|
1041
1411
|
},
|
|
1042
1412
|
resolveResponsesName(node) {
|
|
1043
|
-
return
|
|
1413
|
+
return this.resolveSchemaName(`${node.operationId} Responses`);
|
|
1044
1414
|
},
|
|
1045
1415
|
resolveResponseName(node) {
|
|
1046
|
-
return
|
|
1416
|
+
return this.resolveSchemaName(`${node.operationId} Response`);
|
|
1047
1417
|
},
|
|
1048
1418
|
resolvePathParamsName(node, param) {
|
|
1049
|
-
return
|
|
1419
|
+
return this.resolveParamName(node, param);
|
|
1050
1420
|
},
|
|
1051
1421
|
resolveQueryParamsName(node, param) {
|
|
1052
|
-
return
|
|
1422
|
+
return this.resolveParamName(node, param);
|
|
1053
1423
|
},
|
|
1054
1424
|
resolveHeaderParamsName(node, param) {
|
|
1055
|
-
return
|
|
1425
|
+
return this.resolveParamName(node, param);
|
|
1056
1426
|
}
|
|
1057
1427
|
};
|
|
1058
1428
|
});
|
|
1059
1429
|
//#endregion
|
|
1060
1430
|
//#region src/plugin.ts
|
|
1061
1431
|
/**
|
|
1062
|
-
* Canonical plugin name for `@kubb/plugin-zod
|
|
1432
|
+
* Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
|
|
1433
|
+
* cross-plugin dependency references.
|
|
1063
1434
|
*/
|
|
1064
1435
|
const pluginZodName = "plugin-zod";
|
|
1065
1436
|
/**
|
|
1066
|
-
* Generates Zod
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1437
|
+
* Generates Zod v4 schemas from an OpenAPI spec. Use them to validate API
|
|
1438
|
+
* responses at runtime, build form schemas, or feed back into router libraries
|
|
1439
|
+
* that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-client` and
|
|
1440
|
+
* set the client's `parser: 'zod'` to validate every response automatically.
|
|
1069
1441
|
*
|
|
1070
|
-
* @example
|
|
1442
|
+
* @example
|
|
1071
1443
|
* ```ts
|
|
1072
|
-
* import
|
|
1444
|
+
* import { defineConfig } from 'kubb'
|
|
1445
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1446
|
+
* import { pluginZod } from '@kubb/plugin-zod'
|
|
1447
|
+
*
|
|
1073
1448
|
* export default defineConfig({
|
|
1074
|
-
*
|
|
1449
|
+
* input: { path: './petStore.yaml' },
|
|
1450
|
+
* output: { path: './src/gen' },
|
|
1451
|
+
* plugins: [
|
|
1452
|
+
* pluginTs(),
|
|
1453
|
+
* pluginZod({
|
|
1454
|
+
* output: { path: './zod' },
|
|
1455
|
+
* typed: true,
|
|
1456
|
+
* }),
|
|
1457
|
+
* ],
|
|
1075
1458
|
* })
|
|
1076
1459
|
* ```
|
|
1077
1460
|
*/
|
|
1078
1461
|
const pluginZod = definePlugin((options) => {
|
|
1079
1462
|
const { output = {
|
|
1080
1463
|
path: "zod",
|
|
1081
|
-
|
|
1464
|
+
barrel: { type: "named" }
|
|
1082
1465
|
}, group, exclude = [], include, override = [], typed = false, operations = false, mini = false, guidType = "uuid", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
|
|
1083
|
-
const groupConfig = group
|
|
1084
|
-
...group,
|
|
1085
|
-
name: (ctx) => {
|
|
1086
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
1087
|
-
return `${camelCase(ctx.group)}Controller`;
|
|
1088
|
-
}
|
|
1089
|
-
} : void 0;
|
|
1466
|
+
const groupConfig = createGroupConfig(group);
|
|
1090
1467
|
return {
|
|
1091
1468
|
name: pluginZodName,
|
|
1092
1469
|
options,
|