@kubb/plugin-zod 5.0.0-beta.84 → 5.0.0-beta.86

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