@kubb/plugin-zod 5.0.0-beta.42 → 5.0.0-beta.64

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,6 +1,7 @@
1
1
  import { t as __name } from "./chunk-C0LytTxp.js";
2
2
  import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
3
- import { Const, File, Type, jsxRendererSync } from "@kubb/renderer-jsx";
3
+ import { buildList, buildObject, caseParams, containsCircularRef, extractRefName, lazyGetter, mapSchemaItems, mapSchemaMembers, mapSchemaProperties, objectKey, stringify, stringifyObject, syncSchemaRef, toRegExpString } from "@kubb/ast/utils";
4
+ import { Const, File, Type, jsxRenderer } from "@kubb/renderer-jsx";
4
5
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
5
6
  //#region ../../internals/utils/src/casing.ts
6
7
  /**
@@ -13,117 +14,57 @@ import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
13
14
  function toCamelOrPascal(text, pascal) {
14
15
  return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
15
16
  if (word.length > 1 && word === word.toUpperCase()) return word;
16
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
17
- return word.charAt(0).toUpperCase() + word.slice(1);
17
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
18
18
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
19
19
  }
20
20
  /**
21
- * Splits `text` on `.` and applies `transformPart` to each segment.
22
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
23
- * Segments are joined with `/` to form a file path.
24
- *
25
- * Only splits on dots followed by a letter so that version numbers
26
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
27
- */
28
- function applyToFileParts(text, transformPart) {
29
- const parts = text.split(/\.(?=[a-zA-Z])/);
30
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
31
- }
32
- /**
33
21
  * Converts `text` to camelCase.
34
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
35
22
  *
36
- * @example
37
- * camelCase('hello-world') // 'helloWorld'
38
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
23
+ * @example Word boundaries
24
+ * `camelCase('hello-world') // 'helloWorld'`
25
+ *
26
+ * @example With a prefix
27
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
39
28
  */
40
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
41
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
42
- prefix,
43
- suffix
44
- } : {}));
29
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
45
30
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
46
31
  }
47
32
  /**
48
33
  * Converts `text` to PascalCase.
49
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
50
34
  *
51
- * @example
52
- * pascalCase('hello-world') // 'HelloWorld'
53
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
35
+ * @example Word boundaries
36
+ * `pascalCase('hello-world') // 'HelloWorld'`
37
+ *
38
+ * @example With a suffix
39
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
54
40
  */
55
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
56
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
57
- prefix,
58
- suffix
59
- }) : camelCase(part));
41
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
60
42
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
61
43
  }
62
44
  //#endregion
63
- //#region ../../internals/utils/src/string.ts
45
+ //#region ../../internals/utils/src/fs.ts
64
46
  /**
65
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
66
- * Returns the string unchanged when no balanced quote pair is found.
47
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
48
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
49
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
67
50
  *
68
- * @example
69
- * trimQuotes('"hello"') // 'hello'
70
- * trimQuotes('hello') // 'hello'
71
- */
72
- function trimQuotes(text) {
73
- if (text.length >= 2) {
74
- const first = text[0];
75
- const last = text[text.length - 1];
76
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
77
- }
78
- return text;
79
- }
80
- //#endregion
81
- //#region ../../internals/utils/src/object.ts
82
- /**
83
- * Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
51
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
52
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
53
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
54
+ * absolute path, letting generated files escape the configured output directory.
84
55
  *
85
- * @example
86
- * stringify('hello') // '"hello"'
87
- * stringify('"hello"') // '"hello"'
88
- */
89
- function stringify(value) {
90
- if (value === void 0 || value === null) return "\"\"";
91
- return JSON.stringify(trimQuotes(value.toString()));
92
- }
93
- /**
94
- * Converts a plain object into a multiline key-value string suitable for embedding in generated code.
95
- * Nested objects are recursively stringified with indentation.
56
+ * @example Nested path from a dotted name
57
+ * `toFilePath('pet.petId') // 'pet/petId'`
96
58
  *
97
- * @example
98
- * stringifyObject({ foo: 'bar', nested: { a: 1 } })
99
- * // 'foo: bar,\nnested: {\n a: 1\n }'
100
- */
101
- function stringifyObject(value) {
102
- return Object.entries(value).map(([key, val]) => {
103
- if (val !== null && typeof val === "object") return `${key}: {\n ${stringifyObject(val)}\n }`;
104
- return `${key}: ${val}`;
105
- }).filter(Boolean).join(",\n");
106
- }
107
- //#endregion
108
- //#region ../../internals/utils/src/regexp.ts
109
- /**
110
- * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
111
- * Inline flags expressed as `^(?im)` prefixes are extracted and applied to the resulting expression.
112
- * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
59
+ * @example PascalCase the final segment
60
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
113
61
  *
114
- * @example
115
- * toRegExpString('^(?im)foo') // 'new RegExp("foo", "im")'
116
- * toRegExpString('^(?im)foo', null) // → '/foo/im'
62
+ * @example Suffix applied to the final segment only
63
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
117
64
  */
118
- function toRegExpString(text, func = "RegExp") {
119
- const raw = trimQuotes(text);
120
- const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
121
- const replacementTarget = match?.[1] ?? "";
122
- const matchedFlags = match?.[2];
123
- const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
124
- const { source, flags } = new RegExp(cleaned, matchedFlags);
125
- if (func === null) return `/${source}/${flags}`;
126
- return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
65
+ function toFilePath(name, caseLast = camelCase) {
66
+ const parts = name.split(/\.(?=[a-zA-Z])/);
67
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
127
68
  }
128
69
  //#endregion
129
70
  //#region ../../internals/utils/src/reserved.ts
@@ -305,26 +246,24 @@ function resolveContentTypeVariants(entries, baseName) {
305
246
  * shared default naming so every plugin groups output consistently:
306
247
  *
307
248
  * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
308
- * - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
249
+ * - other groups use the camelCased group (`pet store``petStore`).
309
250
  *
310
251
  * A user-provided `group.name` always wins over the default namer, so callers stay in
311
252
  * control of their output folders. Returns `null` when grouping is disabled, matching the
312
253
  * per-plugin convention.
313
254
  *
314
255
  * @param group - The user-supplied group option, or `undefined` to disable grouping.
315
- * @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
316
256
  *
317
257
  * @example
318
258
  * ```ts
319
- * createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
320
- * createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
259
+ * createGroupConfig(group) // shared across every plugin
321
260
  * ```
322
261
  */
323
- function createGroupConfig(group, options) {
262
+ function createGroupConfig(group) {
324
263
  if (!group) return null;
325
264
  const defaultName = (ctx) => {
326
265
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
327
- return `${camelCase(ctx.group)}${options.suffix}`;
266
+ return camelCase(ctx.group);
328
267
  };
329
268
  return {
330
269
  ...group,
@@ -487,12 +426,12 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
487
426
  if (hasCodec(node)) return true;
488
427
  if (node.type === "ref") {
489
428
  if (!node.ref) return false;
490
- const refName = ast.extractRefName(node.ref);
429
+ const refName = extractRefName(node.ref);
491
430
  if (refName) {
492
431
  if (seen.has(refName)) return false;
493
432
  seen.add(refName);
494
433
  }
495
- const resolved = ast.syncSchemaRef(node);
434
+ const resolved = syncSchemaRef(node);
496
435
  if (resolved.type === "ref") return false;
497
436
  return containsCodec(resolved, seen);
498
437
  }
@@ -504,6 +443,13 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
504
443
  return children.some((child) => containsCodec(child, seen));
505
444
  }
506
445
  /**
446
+ * Collects the names of `$ref` schemas that transitively contain a codec, so the generator can route
447
+ * them to their input (encode) variant.
448
+ */
449
+ function collectCodecRefNames(node) {
450
+ return ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 });
451
+ }
452
+ /**
507
453
  * Collects all resolved schema names for an operation's parameters and responses
508
454
  * into a single lookup object, useful for building imports and type references.
509
455
  */
@@ -628,7 +574,7 @@ function strictOneOfMember$1(member, node) {
628
574
  if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
629
575
  if (node.type === "ref") {
630
576
  if (member.startsWith("z.lazy(")) return member;
631
- const schema = ast.syncSchemaRef(node);
577
+ const schema = syncSchemaRef(node);
632
578
  if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
633
579
  }
634
580
  return member;
@@ -713,24 +659,24 @@ const printerZod = ast.definePrinter((options) => {
713
659
  },
714
660
  ref(node) {
715
661
  if (!node.name) return null;
716
- const refName = node.ref ? ast.extractRefName(node.ref) ?? node.name : node.name;
662
+ const refName = node.ref ? extractRefName(node.ref) ?? node.name : node.name;
717
663
  const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
718
664
  const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.resolveInputSchemaName(refName) ?? refName : this.options.resolver?.default(refName, "function") ?? refName : node.name;
719
665
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
720
666
  return resolvedName;
721
667
  },
722
668
  object(node) {
723
- const objectBase = `z.object({\n ${node.properties.map((prop) => {
724
- const { name: propName, schema } = prop;
725
- const meta = ast.syncSchemaRef(schema);
726
- const isNullable = meta.nullable;
727
- const isOptional = schema.optional;
728
- const isNullish = schema.nullish;
729
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
669
+ const isCyclic = (schema) => this.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
670
+ const objectBase = `z.object(${buildObject(mapSchemaProperties(node, (schema) => {
671
+ const hasSelfRef = isCyclic(schema);
730
672
  const savedCyclicSchemas = this.options.cyclicSchemas;
731
673
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
732
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
674
+ const baseOutput = this.transform(schema) ?? this.transform(ast.factory.createSchema({ type: "unknown" }));
733
675
  if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
676
+ return baseOutput;
677
+ }).map(({ name: propName, property, output: baseOutput }) => {
678
+ const { schema } = property;
679
+ const meta = syncSchemaRef(schema);
734
680
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
735
681
  output: baseOutput,
736
682
  schema
@@ -738,42 +684,41 @@ const printerZod = ast.definePrinter((options) => {
738
684
  const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
739
685
  const value = applyModifiers({
740
686
  value: wrappedOutput,
741
- nullable: isNullable,
742
- optional: isOptional,
743
- nullish: isNullish,
687
+ nullable: meta.nullable,
688
+ optional: schema.optional || property.required === false,
689
+ nullish: schema.nullish,
744
690
  defaultValue: meta.default,
745
691
  description: descriptionToApply
746
692
  });
747
- if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
748
- return `"${propName}": ${value}`;
749
- }).join(",\n ")}\n })`;
693
+ return isCyclic(schema) ? lazyGetter({
694
+ name: propName,
695
+ body: value
696
+ }) : `${objectKey(propName)}: ${value}`;
697
+ }))})`;
750
698
  return (() => {
751
699
  if (node.additionalProperties && node.additionalProperties !== true) {
752
700
  const catchallType = this.transform(node.additionalProperties);
753
701
  return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
754
702
  }
755
- if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.createSchema({ type: "unknown" }))})`;
703
+ if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.factory.createSchema({ type: "unknown" }))})`;
756
704
  if (node.additionalProperties === false) return `${objectBase}.strict()`;
757
705
  return objectBase;
758
706
  })();
759
707
  },
760
708
  array(node) {
761
- const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
709
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
762
710
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
763
711
  },
764
712
  tuple(node) {
765
- return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
713
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
766
714
  },
767
715
  union(node) {
768
716
  const nodeMembers = node.members ?? [];
769
- const members = nodeMembers.map((memberNode) => {
770
- const member = this.transform(memberNode);
771
- return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
772
- }).filter(Boolean);
717
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema) : output).filter(Boolean);
773
718
  if (members.length === 0) return "";
774
719
  if (members.length === 1) return members[0];
775
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
776
- return `z.union([${members.join(", ")}])`;
720
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
721
+ return `z.union(${buildList(members)})`;
777
722
  },
778
723
  intersection(node) {
779
724
  const members = node.members ?? [];
@@ -795,7 +740,7 @@ const printerZod = ast.definePrinter((options) => {
795
740
  const { keysToOmit } = this.options;
796
741
  const transformed = this.transform(node);
797
742
  if (!transformed) return null;
798
- const meta = ast.syncSchemaRef(node);
743
+ const meta = syncSchemaRef(node);
799
744
  return applyModifiers({
800
745
  value: (() => {
801
746
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -892,54 +837,53 @@ const printerZodMini = ast.definePrinter((options) => {
892
837
  },
893
838
  ref(node) {
894
839
  if (!node.name) return null;
895
- const refName = node.ref ? ast.extractRefName(node.ref) ?? node.name : node.name;
840
+ const refName = node.ref ? extractRefName(node.ref) ?? node.name : node.name;
896
841
  const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
897
842
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
898
843
  return resolvedName;
899
844
  },
900
845
  object(node) {
901
- return `z.object({\n ${node.properties.map((prop) => {
902
- const { name: propName, schema } = prop;
903
- const meta = ast.syncSchemaRef(schema);
904
- const isNullable = meta.nullable;
905
- const isOptional = schema.optional;
906
- const isNullish = schema.nullish;
907
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
846
+ const isCyclic = (schema) => this.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
847
+ return `z.object(${buildObject(mapSchemaProperties(node, (schema) => {
848
+ const hasSelfRef = isCyclic(schema);
908
849
  const savedCyclicSchemas = this.options.cyclicSchemas;
909
850
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
910
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
851
+ const baseOutput = this.transform(schema) ?? this.transform(ast.factory.createSchema({ type: "unknown" }));
911
852
  if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
853
+ return baseOutput;
854
+ }).map(({ name: propName, property, output: baseOutput }) => {
855
+ const { schema } = property;
856
+ const meta = syncSchemaRef(schema);
912
857
  const value = applyMiniModifiers({
913
858
  value: this.options.wrapOutput ? this.options.wrapOutput({
914
859
  output: baseOutput,
915
860
  schema
916
861
  }) || baseOutput : baseOutput,
917
- nullable: isNullable,
918
- optional: isOptional,
919
- nullish: isNullish,
862
+ nullable: meta.nullable,
863
+ optional: schema.optional || property.required === false,
864
+ nullish: schema.nullish,
920
865
  defaultValue: meta.default
921
866
  });
922
- if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
923
- return `"${propName}": ${value}`;
924
- }).join(",\n ")}\n })`;
867
+ return isCyclic(schema) ? lazyGetter({
868
+ name: propName,
869
+ body: value
870
+ }) : `${objectKey(propName)}: ${value}`;
871
+ }))})`;
925
872
  },
926
873
  array(node) {
927
- const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
874
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
928
875
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
929
876
  },
930
877
  tuple(node) {
931
- return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
878
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
932
879
  },
933
880
  union(node) {
934
881
  const nodeMembers = node.members ?? [];
935
- const members = nodeMembers.map((memberNode) => {
936
- const member = this.transform(memberNode);
937
- return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
938
- }).filter(Boolean);
882
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
939
883
  if (members.length === 0) return "";
940
884
  if (members.length === 1) return members[0];
941
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
942
- return `z.union([${members.join(", ")}])`;
885
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
886
+ return `z.union(${buildList(members)})`;
943
887
  },
944
888
  intersection(node) {
945
889
  const members = node.members ?? [];
@@ -961,7 +905,7 @@ const printerZodMini = ast.definePrinter((options) => {
961
905
  const { keysToOmit } = this.options;
962
906
  const transformed = this.transform(node);
963
907
  if (!transformed) return null;
964
- const meta = ast.syncSchemaRef(node);
908
+ const meta = syncSchemaRef(node);
965
909
  return applyMiniModifiers({
966
910
  value: (() => {
967
911
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -1037,17 +981,16 @@ function getMiniPrinter(resolver, params) {
1037
981
  */
1038
982
  const zodGenerator = defineGenerator({
1039
983
  name: "zod",
1040
- renderer: jsxRendererSync,
984
+ renderer: jsxRenderer,
1041
985
  schema(node, ctx) {
1042
986
  const { adapter, config, resolver, root } = ctx;
1043
987
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
1044
988
  const dateType = adapter.options.dateType;
1045
989
  if (!node.name) return;
1046
- const mode = ctx.getMode(output);
1047
990
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1048
991
  const cyclicSchemas = new Set(ctx.meta.circularNames);
1049
992
  const hasCodec = !mini && containsCodec(node);
1050
- const codecRefNames = new Set(hasCodec ? ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? ast.extractRefName(n.ref) ?? void 0 : void 0 }) : []);
993
+ const codecRefNames = new Set(hasCodec ? collectCodecRefNames(node) : []);
1051
994
  const importEntries = adapter.getImports(node, (schemaName) => ({
1052
995
  name: resolver.resolveSchemaName(schemaName),
1053
996
  path: resolver.resolveFile({
@@ -1060,7 +1003,7 @@ const zodGenerator = defineGenerator({
1060
1003
  }).path
1061
1004
  }));
1062
1005
  const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
1063
- name: resolver.resolveInputSchemaName(schemaName),
1006
+ name: [resolver.resolveInputSchemaName(schemaName)],
1064
1007
  path: resolver.resolveFile({
1065
1008
  name: schemaName,
1066
1009
  extname: ".ts"
@@ -1129,7 +1072,7 @@ const zodGenerator = defineGenerator({
1129
1072
  path: importPath,
1130
1073
  isNameSpace: isZodImport
1131
1074
  }),
1132
- mode === "split" && imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
1075
+ imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
1133
1076
  root: meta.file.path,
1134
1077
  path: imp.path,
1135
1078
  name: imp.name
@@ -1158,9 +1101,8 @@ const zodGenerator = defineGenerator({
1158
1101
  const { adapter, config, resolver, root } = ctx;
1159
1102
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
1160
1103
  const dateType = adapter.options.dateType;
1161
- const mode = ctx.getMode(output);
1162
1104
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1163
- const params = ast.caseParams(node.parameters, paramsCasing);
1105
+ const params = caseParams(node.parameters, paramsCasing);
1164
1106
  const meta = { file: resolver.resolveFile({
1165
1107
  name: node.operationId,
1166
1108
  extname: ".ts",
@@ -1175,7 +1117,7 @@ const zodGenerator = defineGenerator({
1175
1117
  function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
1176
1118
  if (!schema) return null;
1177
1119
  const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
1178
- const codecRefNames = direction === "input" && !mini ? new Set(ast.collect(schema, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? ast.extractRefName(n.ref) ?? void 0 : void 0 })) : null;
1120
+ const codecRefNames = direction === "input" && !mini ? new Set(collectCodecRefNames(schema)) : null;
1179
1121
  const imports = adapter.getImports(schema, (schemaName) => ({
1180
1122
  name: codecRefNames?.has(schemaName) ? resolver.resolveInputSchemaName(schemaName) : resolver.resolveSchemaName(schemaName),
1181
1123
  path: resolver.resolveFile({
@@ -1217,7 +1159,7 @@ const zodGenerator = defineGenerator({
1217
1159
  cyclicSchemas,
1218
1160
  nodes: printer?.nodes
1219
1161
  })[direction];
1220
- return /* @__PURE__ */ jsxs(Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
1162
+ return /* @__PURE__ */ jsxs(Fragment, { children: [imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
1221
1163
  root: meta.file.path,
1222
1164
  path: imp.path,
1223
1165
  name: imp.name
@@ -1234,9 +1176,9 @@ const zodGenerator = defineGenerator({
1234
1176
  }
1235
1177
  function buildContentTypeVariants(entries, baseName, decorate, direction) {
1236
1178
  const variants = resolveContentTypeVariants(entries, baseName);
1237
- const unionSchema = ast.createSchema({
1179
+ const unionSchema = ast.factory.createSchema({
1238
1180
  type: "union",
1239
- members: variants.map((variant) => ast.createSchema({
1181
+ members: variants.map((variant) => ast.factory.createSchema({
1240
1182
  type: "ref",
1241
1183
  name: variant.name
1242
1184
  }))
@@ -1274,12 +1216,12 @@ const zodGenerator = defineGenerator({
1274
1216
  name: resolver.resolveSchemaName(schemaName),
1275
1217
  path: ""
1276
1218
  })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
1277
- const members = responsesWithSchema.map((res) => ast.createSchema({
1219
+ const members = responsesWithSchema.map((res) => ast.factory.createSchema({
1278
1220
  type: "ref",
1279
1221
  name: resolver.resolveResponseStatusName(node, res.statusCode)
1280
1222
  }));
1281
1223
  return renderSchemaEntry({
1282
- schema: members.length === 1 ? members[0] : ast.createSchema({
1224
+ schema: members.length === 1 ? members[0] : ast.factory.createSchema({
1283
1225
  type: "union",
1284
1226
  members
1285
1227
  }),
@@ -1357,7 +1299,7 @@ const zodGenerator = defineGenerator({
1357
1299
  return {
1358
1300
  node,
1359
1301
  data: buildSchemaNames(node, {
1360
- params: ast.caseParams(node.parameters, paramsCasing),
1302
+ params: caseParams(node.parameters, paramsCasing),
1361
1303
  resolver
1362
1304
  })
1363
1305
  };
@@ -1425,14 +1367,16 @@ const zodGenerator = defineGenerator({
1425
1367
  /**
1426
1368
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
1427
1369
  * paths for every generated Zod schema. Schemas use camelCase with a
1428
- * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase.
1370
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase
1371
+ * with a `SchemaType` suffix (`PetSchemaType`), so the value and the type
1372
+ * never share an identifier even when the schema name is all-uppercase.
1429
1373
  *
1430
1374
  * @example Resolve schema and type names
1431
1375
  * ```ts
1432
1376
  * import { resolverZod } from '@kubb/plugin-zod'
1433
1377
  *
1434
1378
  * resolverZod.default('list pets', 'function') // 'listPetsSchema'
1435
- * resolverZod.resolveSchemaTypeName('pet') // 'PetSchema'
1379
+ * resolverZod.resolveSchemaTypeName('pet') // 'PetSchemaType'
1436
1380
  * ```
1437
1381
  */
1438
1382
  const resolverZod = defineResolver(() => {
@@ -1440,17 +1384,14 @@ const resolverZod = defineResolver(() => {
1440
1384
  name: "default",
1441
1385
  pluginName: "plugin-zod",
1442
1386
  default(name, type) {
1443
- const resolved = camelCase(name, {
1444
- isFile: type === "file",
1445
- suffix: type ? "schema" : void 0
1446
- });
1447
- return type === "file" ? resolved : ensureValidVarName(resolved);
1387
+ if (type === "file") return toFilePath(name, (part) => camelCase(part, { suffix: "schema" }));
1388
+ return ensureValidVarName(camelCase(name, { suffix: type ? "schema" : void 0 }));
1448
1389
  },
1449
1390
  resolveSchemaName(name) {
1450
1391
  return ensureValidVarName(camelCase(name, { suffix: "schema" }));
1451
1392
  },
1452
1393
  resolveSchemaTypeName(name) {
1453
- return ensureValidVarName(pascalCase(name, { suffix: "schema" }));
1394
+ return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
1454
1395
  },
1455
1396
  resolveInputSchemaName(name) {
1456
1397
  return this.resolveSchemaName(`${name} input`);
@@ -1459,7 +1400,7 @@ const resolverZod = defineResolver(() => {
1459
1400
  return this.resolveSchemaTypeName(`${name} input`);
1460
1401
  },
1461
1402
  resolveTypeName(name) {
1462
- return ensureValidVarName(pascalCase(name));
1403
+ return ensureValidVarName(pascalCase(name, { suffix: "type" }));
1463
1404
  },
1464
1405
  resolvePathName(name, type) {
1465
1406
  return this.default(name, type);
@@ -1525,9 +1466,9 @@ const pluginZodName = "plugin-zod";
1525
1466
  const pluginZod = definePlugin((options) => {
1526
1467
  const { output = {
1527
1468
  path: "zod",
1528
- barrelType: "named"
1529
- }, 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;
1530
- const groupConfig = createGroupConfig(group, { suffix: "Controller" });
1469
+ barrel: { type: "named" }
1470
+ }, 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, macros: userMacros, generators: userGenerators = [] } = options;
1471
+ const groupConfig = createGroupConfig(group);
1531
1472
  return {
1532
1473
  name: pluginZodName,
1533
1474
  options,
@@ -1553,7 +1494,7 @@ const pluginZod = definePlugin((options) => {
1553
1494
  ...resolverZod,
1554
1495
  ...userResolver
1555
1496
  } : resolverZod);
1556
- if (userTransformer) ctx.setTransformer(userTransformer);
1497
+ if (userMacros?.length) ctx.setMacros(userMacros);
1557
1498
  ctx.addGenerator(zodGenerator);
1558
1499
  for (const gen of userGenerators) ctx.addGenerator(gen);
1559
1500
  } }