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