@kubb/plugin-zod 5.0.0-beta.30 → 5.0.0-beta.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +312 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/index.js +312 -78
- package/dist/index.js.map +1 -1
- package/extension.yaml +2 -0
- package/package.json +5 -4
- package/src/components/Operations.tsx +2 -1
- package/src/generators/zodGenerator.tsx +166 -61
- package/src/plugin.ts +3 -13
- package/src/printers/printerZod.ts +24 -5
- package/src/resolvers/resolverZod.ts +6 -0
- package/src/types.ts +15 -0
- package/src/utils.ts +93 -0
package/dist/index.cjs
CHANGED
|
@@ -259,6 +259,89 @@ function ensureValidVarName(name) {
|
|
|
259
259
|
return `_${name}`;
|
|
260
260
|
}
|
|
261
261
|
//#endregion
|
|
262
|
+
//#region ../../internals/shared/src/operation.ts
|
|
263
|
+
/**
|
|
264
|
+
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
265
|
+
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
266
|
+
*/
|
|
267
|
+
function getContentTypeSuffix(contentType) {
|
|
268
|
+
const baseType = contentType.split(";")[0].trim();
|
|
269
|
+
if (baseType === "application/json") return "Json";
|
|
270
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
271
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
272
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
273
|
+
if (parts.length === 0) return "Unknown";
|
|
274
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
278
|
+
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
279
|
+
*/
|
|
280
|
+
function getPerContentTypeName(baseName, suffix) {
|
|
281
|
+
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
282
|
+
return baseName + suffix;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Resolves per-content-type variant names for a set of content entries, deduplicating suffix
|
|
286
|
+
* collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
|
|
287
|
+
* the final (possibly counter-augmented) value, so callers can derive parallel names in another
|
|
288
|
+
* namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
|
|
289
|
+
*/
|
|
290
|
+
function resolveContentTypeVariants(entries, baseName) {
|
|
291
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
292
|
+
return entries.filter((entry) => entry.schema).map((entry) => {
|
|
293
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
294
|
+
let suffix = baseSuffix;
|
|
295
|
+
let name = getPerContentTypeName(baseName, suffix);
|
|
296
|
+
let counter = 2;
|
|
297
|
+
while (usedNames.has(name)) {
|
|
298
|
+
suffix = `${baseSuffix}${counter++}`;
|
|
299
|
+
name = getPerContentTypeName(baseName, suffix);
|
|
300
|
+
}
|
|
301
|
+
usedNames.add(name);
|
|
302
|
+
return {
|
|
303
|
+
name,
|
|
304
|
+
suffix,
|
|
305
|
+
schema: entry.schema,
|
|
306
|
+
keysToOmit: entry.keysToOmit,
|
|
307
|
+
contentType: entry.contentType
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region ../../internals/shared/src/group.ts
|
|
313
|
+
/**
|
|
314
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
315
|
+
* shared default naming so every plugin groups output consistently:
|
|
316
|
+
*
|
|
317
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
318
|
+
* - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
|
|
319
|
+
*
|
|
320
|
+
* Returns `null` when grouping is disabled, matching the per-plugin convention.
|
|
321
|
+
*
|
|
322
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
323
|
+
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
324
|
+
* @param options.honorName - When `true`, a user-provided `group.name` overrides the default namer.
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```ts
|
|
328
|
+
* createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-zod
|
|
329
|
+
* createGroupConfig(group, { suffix: 'Controller', honorName: true }) // plugin-faker, plugin-client, …
|
|
330
|
+
* createGroupConfig(group, { suffix: 'Requests', honorName: true }) // plugin-cypress, plugin-mcp
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
function createGroupConfig(group, options) {
|
|
334
|
+
if (!group) return null;
|
|
335
|
+
const defaultName = (ctx) => {
|
|
336
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
337
|
+
return `${camelCase(ctx.group)}${options.suffix}`;
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
...group,
|
|
341
|
+
name: options.honorName && group.name ? group.name : defaultName
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
262
345
|
//#region src/components/Operations.tsx
|
|
263
346
|
function Operations({ name, operations }) {
|
|
264
347
|
const operationsJSON = operations.reduce((prev, acc) => {
|
|
@@ -266,6 +349,7 @@ function Operations({ name, operations }) {
|
|
|
266
349
|
return prev;
|
|
267
350
|
}, {});
|
|
268
351
|
const pathsJSON = operations.reduce((prev, acc) => {
|
|
352
|
+
if (!_kubb_core.ast.isHttpOperationNode(acc.node)) return prev;
|
|
269
353
|
prev[`"${acc.node.path}"`] = {
|
|
270
354
|
...prev[`"${acc.node.path}"`] ?? {},
|
|
271
355
|
[acc.node.method]: `operations["${acc.node.operationId}"]`
|
|
@@ -375,6 +459,61 @@ function shouldCoerce(coercion, type) {
|
|
|
375
459
|
return !!coercion[type];
|
|
376
460
|
}
|
|
377
461
|
/**
|
|
462
|
+
* Registered codecs, checked in order.
|
|
463
|
+
*/
|
|
464
|
+
const codecs = [{
|
|
465
|
+
matches(node) {
|
|
466
|
+
return node.type === "date" && node.representation === "date";
|
|
467
|
+
},
|
|
468
|
+
decode(node) {
|
|
469
|
+
return node.format === "date" ? "z.iso.date().transform((value) => new Date(value))" : "z.iso.datetime().transform((value) => new Date(value))";
|
|
470
|
+
},
|
|
471
|
+
encode(node) {
|
|
472
|
+
return node.format === "date" ? "z.date().transform((value) => value.toISOString().slice(0, 10))" : "z.date().transform((value) => value.toISOString())";
|
|
473
|
+
}
|
|
474
|
+
}];
|
|
475
|
+
/**
|
|
476
|
+
* Returns the codec for this node, or `undefined` when the node needs no
|
|
477
|
+
* encode/decode (its wire and runtime types match).
|
|
478
|
+
*/
|
|
479
|
+
function getCodec(node) {
|
|
480
|
+
if (!node) return void 0;
|
|
481
|
+
return codecs.find((codec) => codec.matches(node));
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Returns `true` when the node itself is encoded/decoded by a codec.
|
|
485
|
+
*/
|
|
486
|
+
function hasCodec(node) {
|
|
487
|
+
return getCodec(node) !== void 0;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Returns `true` when the schema transitively contains a codec node —
|
|
491
|
+
* a value whose runtime type differs from its wire type (see {@link hasCodec}),
|
|
492
|
+
* so it must be decoded (response) or encoded (request) at the validation boundary.
|
|
493
|
+
* `$ref`s are followed via their resolved schema; a `seen` set guards cycles.
|
|
494
|
+
*/
|
|
495
|
+
function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
|
|
496
|
+
if (!node) return false;
|
|
497
|
+
if (hasCodec(node)) return true;
|
|
498
|
+
if (node.type === "ref") {
|
|
499
|
+
if (!node.ref) return false;
|
|
500
|
+
const refName = _kubb_core.ast.extractRefName(node.ref);
|
|
501
|
+
if (refName) {
|
|
502
|
+
if (seen.has(refName)) return false;
|
|
503
|
+
seen.add(refName);
|
|
504
|
+
}
|
|
505
|
+
const resolved = _kubb_core.ast.syncSchemaRef(node);
|
|
506
|
+
if (resolved.type === "ref") return false;
|
|
507
|
+
return containsCodec(resolved, seen);
|
|
508
|
+
}
|
|
509
|
+
const children = [];
|
|
510
|
+
if ("properties" in node && node.properties) children.push(...node.properties.map((prop) => prop.schema));
|
|
511
|
+
if ("items" in node && node.items) children.push(...node.items);
|
|
512
|
+
if ("members" in node && node.members) children.push(...node.members);
|
|
513
|
+
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
514
|
+
return children.some((child) => containsCodec(child, seen));
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
378
517
|
* Collects all resolved schema names for an operation's parameters and responses
|
|
379
518
|
* into a single lookup object, useful for building imports and type references.
|
|
380
519
|
*/
|
|
@@ -546,8 +685,9 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
|
|
|
546
685
|
return shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.bigint()" : "z.bigint()";
|
|
547
686
|
},
|
|
548
687
|
date(node) {
|
|
549
|
-
|
|
550
|
-
return
|
|
688
|
+
const codec = getCodec(node);
|
|
689
|
+
if (codec) return this.options.direction === "input" ? codec.encode(node) : codec.decode(node);
|
|
690
|
+
return "z.iso.date()";
|
|
551
691
|
},
|
|
552
692
|
datetime(node) {
|
|
553
693
|
const offset = node.offset || this.options.dateType === "stringOffset";
|
|
@@ -584,7 +724,8 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
|
|
|
584
724
|
ref(node) {
|
|
585
725
|
if (!node.name) return null;
|
|
586
726
|
const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
|
|
587
|
-
const
|
|
727
|
+
const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
|
|
728
|
+
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.resolveInputSchemaName(refName) ?? refName : this.options.resolver?.default(refName, "function") ?? refName : node.name;
|
|
588
729
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
|
|
589
730
|
return resolvedName;
|
|
590
731
|
},
|
|
@@ -851,6 +992,54 @@ const printerZodMini = _kubb_core.ast.definePrinter((options) => {
|
|
|
851
992
|
const zodPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
852
993
|
const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
853
994
|
/**
|
|
995
|
+
* Returns the cached `output`/`input` direction printers for a resolver, building them on
|
|
996
|
+
* first use. The `input` printer encodes `Date → string` for request bodies; `output` decodes
|
|
997
|
+
* `string → Date` for responses. Schemas without `dateType: 'date'` fields print identically.
|
|
998
|
+
*/
|
|
999
|
+
function getStdPrinters(resolver, params) {
|
|
1000
|
+
const cached = zodPrinterCache.get(resolver);
|
|
1001
|
+
if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.dateType === params.dateType) return {
|
|
1002
|
+
output: cached.output,
|
|
1003
|
+
input: cached.input
|
|
1004
|
+
};
|
|
1005
|
+
const base = {
|
|
1006
|
+
...params,
|
|
1007
|
+
resolver
|
|
1008
|
+
};
|
|
1009
|
+
const output = printerZod({
|
|
1010
|
+
...base,
|
|
1011
|
+
direction: "output"
|
|
1012
|
+
});
|
|
1013
|
+
const input = printerZod({
|
|
1014
|
+
...base,
|
|
1015
|
+
direction: "input"
|
|
1016
|
+
});
|
|
1017
|
+
zodPrinterCache.set(resolver, {
|
|
1018
|
+
output,
|
|
1019
|
+
input,
|
|
1020
|
+
coercion: params.coercion,
|
|
1021
|
+
guidType: params.guidType,
|
|
1022
|
+
dateType: params.dateType
|
|
1023
|
+
});
|
|
1024
|
+
return {
|
|
1025
|
+
output,
|
|
1026
|
+
input
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
function getMiniPrinter(resolver, params) {
|
|
1030
|
+
const cached = zodMiniPrinterCache.get(resolver);
|
|
1031
|
+
if (cached && cached.guidType === params.guidType) return cached.printer;
|
|
1032
|
+
const p = printerZodMini({
|
|
1033
|
+
...params,
|
|
1034
|
+
resolver
|
|
1035
|
+
});
|
|
1036
|
+
zodMiniPrinterCache.set(resolver, {
|
|
1037
|
+
printer: p,
|
|
1038
|
+
guidType: params.guidType
|
|
1039
|
+
});
|
|
1040
|
+
return p;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
854
1043
|
* Built-in generator for `@kubb/plugin-zod`. Emits one Zod schema per
|
|
855
1044
|
* schema in the spec plus per-operation request/response/parameter schemas.
|
|
856
1045
|
* When `mini: true`, schemas use the Zod Mini functional API instead of
|
|
@@ -866,7 +1055,10 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
866
1055
|
if (!node.name) return;
|
|
867
1056
|
const mode = ctx.getMode(output);
|
|
868
1057
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
869
|
-
const
|
|
1058
|
+
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
1059
|
+
const hasCodec = !mini && containsCodec(node);
|
|
1060
|
+
const codecRefNames = new Set(hasCodec ? _kubb_core.ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? _kubb_core.ast.extractRefName(n.ref) ?? void 0 : void 0 }) : []);
|
|
1061
|
+
const importEntries = adapter.getImports(node, (schemaName) => ({
|
|
870
1062
|
name: resolver.resolveSchemaName(schemaName),
|
|
871
1063
|
path: resolver.resolveFile({
|
|
872
1064
|
name: schemaName,
|
|
@@ -877,6 +1069,24 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
877
1069
|
group: group ?? void 0
|
|
878
1070
|
}).path
|
|
879
1071
|
}));
|
|
1072
|
+
const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
|
|
1073
|
+
name: resolver.resolveInputSchemaName(schemaName),
|
|
1074
|
+
path: resolver.resolveFile({
|
|
1075
|
+
name: schemaName,
|
|
1076
|
+
extname: ".ts"
|
|
1077
|
+
}, {
|
|
1078
|
+
root,
|
|
1079
|
+
output,
|
|
1080
|
+
group: group ?? void 0
|
|
1081
|
+
}).path
|
|
1082
|
+
})) : [];
|
|
1083
|
+
const seenImports = /* @__PURE__ */ new Set();
|
|
1084
|
+
const imports = [...importEntries, ...inputImportEntries].filter((imp) => {
|
|
1085
|
+
const key = `${Array.isArray(imp.name) ? imp.name.join(",") : imp.name}|${imp.path}`;
|
|
1086
|
+
if (seenImports.has(key)) return false;
|
|
1087
|
+
seenImports.add(key);
|
|
1088
|
+
return true;
|
|
1089
|
+
});
|
|
880
1090
|
const meta = {
|
|
881
1091
|
name: resolver.resolveSchemaName(node.name),
|
|
882
1092
|
file: resolver.resolveFile({
|
|
@@ -889,44 +1099,20 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
889
1099
|
})
|
|
890
1100
|
};
|
|
891
1101
|
const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : null;
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
zodPrinterCache.set(resolver, {
|
|
907
|
-
printer: p,
|
|
908
|
-
coercion,
|
|
909
|
-
guidType,
|
|
910
|
-
dateType
|
|
911
|
-
});
|
|
912
|
-
return p;
|
|
913
|
-
}
|
|
914
|
-
function getCachedMiniPrinter() {
|
|
915
|
-
const cached = zodMiniPrinterCache.get(resolver);
|
|
916
|
-
if (cached && cached.guidType === guidType) return cached.printer;
|
|
917
|
-
const p = printerZodMini({
|
|
918
|
-
guidType,
|
|
919
|
-
wrapOutput,
|
|
920
|
-
resolver,
|
|
921
|
-
cyclicSchemas,
|
|
922
|
-
nodes: printer?.nodes
|
|
923
|
-
});
|
|
924
|
-
zodMiniPrinterCache.set(resolver, {
|
|
925
|
-
printer: p,
|
|
926
|
-
guidType
|
|
927
|
-
});
|
|
928
|
-
return p;
|
|
929
|
-
}
|
|
1102
|
+
const stdPrinters = mini ? null : getStdPrinters(resolver, {
|
|
1103
|
+
coercion,
|
|
1104
|
+
guidType,
|
|
1105
|
+
dateType,
|
|
1106
|
+
wrapOutput,
|
|
1107
|
+
cyclicSchemas,
|
|
1108
|
+
nodes: printer?.nodes
|
|
1109
|
+
});
|
|
1110
|
+
const schemaPrinter = mini ? getMiniPrinter(resolver, {
|
|
1111
|
+
guidType,
|
|
1112
|
+
wrapOutput,
|
|
1113
|
+
cyclicSchemas,
|
|
1114
|
+
nodes: printer?.nodes
|
|
1115
|
+
}) : stdPrinters.output;
|
|
930
1116
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
931
1117
|
baseName: meta.file.baseName,
|
|
932
1118
|
path: meta.file.path,
|
|
@@ -957,17 +1143,28 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
957
1143
|
root: meta.file.path,
|
|
958
1144
|
path: imp.path,
|
|
959
1145
|
name: imp.name
|
|
960
|
-
}, [
|
|
1146
|
+
}, [
|
|
1147
|
+
node.name,
|
|
1148
|
+
imp.path,
|
|
1149
|
+
imp.name
|
|
1150
|
+
].join("-"))),
|
|
961
1151
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Zod, {
|
|
962
1152
|
name: meta.name,
|
|
963
1153
|
node,
|
|
964
1154
|
printer: schemaPrinter,
|
|
965
1155
|
inferTypeName
|
|
1156
|
+
}),
|
|
1157
|
+
hasCodec && stdPrinters && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Zod, {
|
|
1158
|
+
name: resolver.resolveInputSchemaName(node.name),
|
|
1159
|
+
node,
|
|
1160
|
+
printer: stdPrinters.input,
|
|
1161
|
+
inferTypeName: inferred ? resolver.resolveInputSchemaTypeName(node.name) : null
|
|
966
1162
|
})
|
|
967
1163
|
]
|
|
968
1164
|
});
|
|
969
1165
|
},
|
|
970
1166
|
operation(node, ctx) {
|
|
1167
|
+
if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
|
|
971
1168
|
const { adapter, config, resolver, root } = ctx;
|
|
972
1169
|
const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
|
|
973
1170
|
const dateType = adapter.options.dateType;
|
|
@@ -985,11 +1182,12 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
985
1182
|
group: group ?? void 0
|
|
986
1183
|
}) };
|
|
987
1184
|
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
988
|
-
function renderSchemaEntry({ schema, name, keysToOmit }) {
|
|
1185
|
+
function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
|
|
989
1186
|
if (!schema) return null;
|
|
990
1187
|
const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
|
|
1188
|
+
const codecRefNames = direction === "input" && !mini ? new Set(_kubb_core.ast.collect(schema, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? _kubb_core.ast.extractRefName(n.ref) ?? void 0 : void 0 })) : null;
|
|
991
1189
|
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
992
|
-
name: resolver.resolveSchemaName(schemaName),
|
|
1190
|
+
name: codecRefNames?.has(schemaName) ? resolver.resolveInputSchemaName(schemaName) : resolver.resolveSchemaName(schemaName),
|
|
993
1191
|
path: resolver.resolveFile({
|
|
994
1192
|
name: schemaName,
|
|
995
1193
|
extname: ".ts"
|
|
@@ -999,8 +1197,6 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
999
1197
|
group: group ?? void 0
|
|
1000
1198
|
}).path
|
|
1001
1199
|
}));
|
|
1002
|
-
const cachedStd = zodPrinterCache.get(resolver);
|
|
1003
|
-
const cachedMini = zodMiniPrinterCache.get(resolver);
|
|
1004
1200
|
const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
|
|
1005
1201
|
guidType,
|
|
1006
1202
|
wrapOutput,
|
|
@@ -1008,10 +1204,9 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1008
1204
|
keysToOmit,
|
|
1009
1205
|
cyclicSchemas,
|
|
1010
1206
|
nodes: printer?.nodes
|
|
1011
|
-
}) :
|
|
1207
|
+
}) : getMiniPrinter(resolver, {
|
|
1012
1208
|
guidType,
|
|
1013
1209
|
wrapOutput,
|
|
1014
|
-
resolver,
|
|
1015
1210
|
cyclicSchemas,
|
|
1016
1211
|
nodes: printer?.nodes
|
|
1017
1212
|
}) : keysToOmit?.length ? printerZod({
|
|
@@ -1022,16 +1217,16 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1022
1217
|
resolver,
|
|
1023
1218
|
keysToOmit,
|
|
1024
1219
|
cyclicSchemas,
|
|
1025
|
-
nodes: printer?.nodes
|
|
1026
|
-
|
|
1220
|
+
nodes: printer?.nodes,
|
|
1221
|
+
direction
|
|
1222
|
+
}) : getStdPrinters(resolver, {
|
|
1027
1223
|
coercion,
|
|
1028
1224
|
guidType,
|
|
1029
1225
|
dateType,
|
|
1030
1226
|
wrapOutput,
|
|
1031
|
-
resolver,
|
|
1032
1227
|
cyclicSchemas,
|
|
1033
1228
|
nodes: printer?.nodes
|
|
1034
|
-
});
|
|
1229
|
+
})[direction];
|
|
1035
1230
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1036
1231
|
root: meta.file.path,
|
|
1037
1232
|
path: imp.path,
|
|
@@ -1047,22 +1242,48 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1047
1242
|
inferTypeName
|
|
1048
1243
|
})] });
|
|
1049
1244
|
}
|
|
1245
|
+
function buildContentTypeVariants(entries, baseName, decorate, direction) {
|
|
1246
|
+
const variants = resolveContentTypeVariants(entries, baseName);
|
|
1247
|
+
const unionSchema = _kubb_core.ast.createSchema({
|
|
1248
|
+
type: "union",
|
|
1249
|
+
members: variants.map((variant) => _kubb_core.ast.createSchema({
|
|
1250
|
+
type: "ref",
|
|
1251
|
+
name: variant.name
|
|
1252
|
+
}))
|
|
1253
|
+
});
|
|
1254
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [variants.map((variant) => renderSchemaEntry({
|
|
1255
|
+
schema: decorate ? decorate(variant.schema) : variant.schema,
|
|
1256
|
+
name: variant.name,
|
|
1257
|
+
keysToOmit: variant.keysToOmit,
|
|
1258
|
+
direction
|
|
1259
|
+
})), renderSchemaEntry({
|
|
1260
|
+
schema: unionSchema,
|
|
1261
|
+
name: baseName,
|
|
1262
|
+
direction
|
|
1263
|
+
})] });
|
|
1264
|
+
}
|
|
1050
1265
|
const paramSchemas = params.map((param) => renderSchemaEntry({
|
|
1051
1266
|
schema: param.schema,
|
|
1052
|
-
name: resolver.resolveParamName(node, param)
|
|
1267
|
+
name: resolver.resolveParamName(node, param),
|
|
1268
|
+
direction: "input"
|
|
1053
1269
|
}));
|
|
1054
|
-
const responseSchemas = node.responses.map((res) =>
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1270
|
+
const responseSchemas = node.responses.map((res) => {
|
|
1271
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1272
|
+
if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.resolveResponseStatusName(node, res.statusCode));
|
|
1273
|
+
const primary = variants[0] ?? res.content?.[0];
|
|
1274
|
+
return renderSchemaEntry({
|
|
1275
|
+
schema: primary?.schema ?? null,
|
|
1276
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
1277
|
+
keysToOmit: primary?.keysToOmit
|
|
1278
|
+
});
|
|
1279
|
+
});
|
|
1280
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
1060
1281
|
const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
|
|
1061
1282
|
const responseUnionName = resolver.resolveResponseName(node);
|
|
1062
|
-
if (new Set(responsesWithSchema.flatMap((res) => res.content
|
|
1283
|
+
if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
|
|
1063
1284
|
name: resolver.resolveSchemaName(schemaName),
|
|
1064
1285
|
path: ""
|
|
1065
|
-
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseUnionName)) return null;
|
|
1286
|
+
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
|
|
1066
1287
|
const members = responsesWithSchema.map((res) => _kubb_core.ast.createSchema({
|
|
1067
1288
|
type: "ref",
|
|
1068
1289
|
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
@@ -1075,14 +1296,27 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1075
1296
|
name: responseUnionName
|
|
1076
1297
|
});
|
|
1077
1298
|
})() : null;
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1299
|
+
const requestBodyContent = node.requestBody?.content ?? [];
|
|
1300
|
+
const requestSchema = (() => {
|
|
1301
|
+
if (requestBodyContent.length === 0) return null;
|
|
1302
|
+
if (requestBodyContent.length === 1) {
|
|
1303
|
+
const entry = requestBodyContent[0];
|
|
1304
|
+
if (!entry.schema) return null;
|
|
1305
|
+
return renderSchemaEntry({
|
|
1306
|
+
schema: {
|
|
1307
|
+
...entry.schema,
|
|
1308
|
+
description: node.requestBody.description ?? entry.schema.description
|
|
1309
|
+
},
|
|
1310
|
+
name: resolver.resolveDataName(node),
|
|
1311
|
+
keysToOmit: entry.keysToOmit,
|
|
1312
|
+
direction: "input"
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
return buildContentTypeVariants(requestBodyContent, resolver.resolveDataName(node), (schema) => ({
|
|
1316
|
+
...schema,
|
|
1317
|
+
description: node.requestBody.description ?? schema.description
|
|
1318
|
+
}), "input");
|
|
1319
|
+
})();
|
|
1086
1320
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
1087
1321
|
baseName: meta.file.baseName,
|
|
1088
1322
|
path: meta.file.path,
|
|
@@ -1129,7 +1363,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1129
1363
|
output,
|
|
1130
1364
|
group: group ?? void 0
|
|
1131
1365
|
}) };
|
|
1132
|
-
const transformedOperations = nodes.map((node) => {
|
|
1366
|
+
const transformedOperations = nodes.filter(_kubb_core.ast.isHttpOperationNode).map((node) => {
|
|
1133
1367
|
return {
|
|
1134
1368
|
node,
|
|
1135
1369
|
data: buildSchemaNames(node, {
|
|
@@ -1228,6 +1462,12 @@ const resolverZod = (0, _kubb_core.defineResolver)(() => {
|
|
|
1228
1462
|
resolveSchemaTypeName(name) {
|
|
1229
1463
|
return ensureValidVarName(pascalCase(name, { suffix: "schema" }));
|
|
1230
1464
|
},
|
|
1465
|
+
resolveInputSchemaName(name) {
|
|
1466
|
+
return this.resolveSchemaName(`${name} input`);
|
|
1467
|
+
},
|
|
1468
|
+
resolveInputSchemaTypeName(name) {
|
|
1469
|
+
return this.resolveSchemaTypeName(`${name} input`);
|
|
1470
|
+
},
|
|
1231
1471
|
resolveTypeName(name) {
|
|
1232
1472
|
return ensureValidVarName(pascalCase(name));
|
|
1233
1473
|
},
|
|
@@ -1297,13 +1537,7 @@ const pluginZod = (0, _kubb_core.definePlugin)((options) => {
|
|
|
1297
1537
|
path: "zod",
|
|
1298
1538
|
barrelType: "named"
|
|
1299
1539
|
}, 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;
|
|
1300
|
-
const groupConfig = group
|
|
1301
|
-
...group,
|
|
1302
|
-
name: (ctx) => {
|
|
1303
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
1304
|
-
return `${camelCase(ctx.group)}Controller`;
|
|
1305
|
-
}
|
|
1306
|
-
} : null;
|
|
1540
|
+
const groupConfig = createGroupConfig(group, { suffix: "Controller" });
|
|
1307
1541
|
return {
|
|
1308
1542
|
name: pluginZodName,
|
|
1309
1543
|
options,
|