@kubb/plugin-ts 5.0.0-beta.42 → 5.0.0-beta.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  enumerable: true
29
29
  }) : target, mod));
30
30
  //#endregion
31
+ let _kubb_ast_utils = require("@kubb/ast/utils");
31
32
  let _kubb_parser_ts = require("@kubb/parser-ts");
32
33
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
33
34
  let _kubb_core = require("@kubb/core");
@@ -45,58 +46,41 @@ let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
45
46
  function toCamelOrPascal(text, pascal) {
46
47
  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) => {
47
48
  if (word.length > 1 && word === word.toUpperCase()) return word;
48
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
49
- return word.charAt(0).toUpperCase() + word.slice(1);
49
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
50
50
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
51
51
  }
52
52
  /**
53
- * Splits `text` on `.` and applies `transformPart` to each segment.
54
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
55
- * Segments are joined with `/` to form a file path.
56
- *
57
- * Only splits on dots followed by a letter so that version numbers
58
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
59
- */
60
- function applyToFileParts(text, transformPart) {
61
- const parts = text.split(/\.(?=[a-zA-Z])/);
62
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
63
- }
64
- /**
65
53
  * Converts `text` to camelCase.
66
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
67
54
  *
68
- * @example
69
- * camelCase('hello-world') // 'helloWorld'
70
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
55
+ * @example Word boundaries
56
+ * `camelCase('hello-world') // 'helloWorld'`
57
+ *
58
+ * @example With a prefix
59
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
71
60
  */
72
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
73
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
74
- prefix,
75
- suffix
76
- } : {}));
61
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
77
62
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
78
63
  }
79
64
  /**
80
65
  * Converts `text` to PascalCase.
81
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
82
66
  *
83
- * @example
84
- * pascalCase('hello-world') // 'HelloWorld'
85
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
67
+ * @example Word boundaries
68
+ * `pascalCase('hello-world') // 'HelloWorld'`
69
+ *
70
+ * @example With a suffix
71
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
86
72
  */
87
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
88
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
89
- prefix,
90
- suffix
91
- }) : camelCase(part));
73
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
92
74
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
93
75
  }
94
76
  /**
95
77
  * Converts `text` to snake_case.
96
78
  *
97
- * @example
98
- * snakeCase('helloWorld') // 'hello_world'
99
- * snakeCase('Hello-World') // 'hello_world'
79
+ * @example From camelCase
80
+ * `snakeCase('helloWorld') // 'hello_world'`
81
+ *
82
+ * @example From mixed separators
83
+ * `snakeCase('Hello-World') // 'hello_world'`
100
84
  */
101
85
  function snakeCase(text, { prefix = "", suffix = "" } = {}) {
102
86
  return `${prefix} ${text} ${suffix}`.trim().replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s\-.]+/g, "_").replace(/[^a-zA-Z0-9_]/g, "").toLowerCase().split("_").filter(Boolean).join("_");
@@ -104,8 +88,8 @@ function snakeCase(text, { prefix = "", suffix = "" } = {}) {
104
88
  /**
105
89
  * Converts `text` to SCREAMING_SNAKE_CASE.
106
90
  *
107
- * @example
108
- * screamingSnakeCase('helloWorld') // 'HELLO_WORLD'
91
+ * @example From camelCase
92
+ * `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`
109
93
  */
110
94
  function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
111
95
  return snakeCase(text, {
@@ -114,60 +98,29 @@ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
114
98
  }).toUpperCase();
115
99
  }
116
100
  //#endregion
117
- //#region ../../internals/utils/src/string.ts
101
+ //#region ../../internals/utils/src/fs.ts
118
102
  /**
119
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
120
- * Returns the string unchanged when no balanced quote pair is found.
103
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
104
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
105
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
121
106
  *
122
- * @example
123
- * trimQuotes('"hello"') // 'hello'
124
- * trimQuotes('hello') // 'hello'
125
- */
126
- function trimQuotes(text) {
127
- if (text.length >= 2) {
128
- const first = text[0];
129
- const last = text[text.length - 1];
130
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
131
- }
132
- return text;
133
- }
134
- /**
135
- * Escapes characters that are not allowed inside JS string literals.
136
- * Handles quotes, backslashes, and Unicode line terminators (U+2028 / U+2029).
107
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
108
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
109
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
110
+ * absolute path, letting generated files escape the configured output directory.
137
111
  *
138
- * @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
112
+ * @example Nested path from a dotted name
113
+ * `toFilePath('pet.petId') // 'pet/petId'`
139
114
  *
140
- * @example
141
- * ```ts
142
- * jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
143
- * ```
144
- */
145
- function jsStringEscape(input) {
146
- return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
147
- switch (character) {
148
- case "\"":
149
- case "'":
150
- case "\\": return `\\${character}`;
151
- case "\n": return "\\n";
152
- case "\r": return "\\r";
153
- case "\u2028": return "\\u2028";
154
- case "\u2029": return "\\u2029";
155
- default: return "";
156
- }
157
- });
158
- }
159
- //#endregion
160
- //#region ../../internals/utils/src/object.ts
161
- /**
162
- * Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
115
+ * @example PascalCase the final segment
116
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
163
117
  *
164
- * @example
165
- * stringify('hello') // '"hello"'
166
- * stringify('"hello"') // '"hello"'
118
+ * @example Suffix applied to the final segment only
119
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
167
120
  */
168
- function stringify(value) {
169
- if (value === void 0 || value === null) return "\"\"";
170
- return JSON.stringify(trimQuotes(value.toString()));
121
+ function toFilePath(name, caseLast = camelCase) {
122
+ const parts = name.split(/\.(?=[a-zA-Z])/);
123
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
171
124
  }
172
125
  //#endregion
173
126
  //#region ../../internals/utils/src/reserved.ts
@@ -303,26 +256,24 @@ const OPTIONAL_ADDS_UNDEFINED = new Set(["undefined", "questionTokenAndUndefined
303
256
  */
304
257
  const OPTIONAL_ADDS_QUESTION_TOKEN = new Set(["questionToken", "questionTokenAndUndefined"]);
305
258
  /**
306
- * `enumType` values that append a `Key` suffix to the generated enum type alias.
259
+ * `enum.type` values that append a `typeSuffix` to the generated enum type alias.
307
260
  */
308
- const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst", "asPascalConst"]);
261
+ const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst"]);
309
262
  /**
310
- * `enumType` values that require a runtime value declaration (object, enum, or literal).
263
+ * `enum.type` values that require a runtime value declaration (object, enum, or literal).
311
264
  */
312
265
  const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set([
313
266
  "enum",
314
267
  "asConst",
315
- "asPascalConst",
316
268
  "constEnum",
317
269
  "literal",
318
270
  void 0
319
271
  ]);
320
272
  /**
321
- * `enumType` values whose type declaration is type-only (no runtime value emitted for the type alias).
273
+ * `enum.type` values whose type declaration is type-only (no runtime value emitted for the type alias).
322
274
  */
323
275
  const ENUM_TYPES_WITH_TYPE_ONLY = new Set([
324
276
  "asConst",
325
- "asPascalConst",
326
277
  "literal",
327
278
  void 0
328
279
  ]);
@@ -642,12 +593,10 @@ const createStringLiteral = factory.createStringLiteral;
642
593
  * Creates an array type node (e.g., `T[]`).
643
594
  */
644
595
  const createArrayTypeNode = factory.createArrayTypeNode;
645
- factory.createParenthesizedType;
646
596
  /**
647
597
  * Creates a literal type node (e.g., `'hello'`, `42`, `true`).
648
598
  */
649
599
  const createLiteralTypeNode = factory.createLiteralTypeNode;
650
- factory.createNull;
651
600
  /**
652
601
  * Creates an identifier node.
653
602
  */
@@ -672,8 +621,6 @@ const createTrue = factory.createTrue;
672
621
  * Creates a boolean false literal type node.
673
622
  */
674
623
  const createFalse = factory.createFalse;
675
- factory.createIndexedAccessTypeNode;
676
- factory.createTypeOperatorNode;
677
624
  /**
678
625
  * Creates a prefix unary expression (e.g., negative numbers, logical not).
679
626
  */
@@ -754,41 +701,44 @@ function buildIndexSignatures(node, propertyCount, print) {
754
701
  * Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
755
702
  *
756
703
  * The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
757
- * valid TypeScript identifier. The resolver normalizes it; for inline enum
758
- * properties the adapter already emits a PascalCase+suffix name so resolution is typically a no-op.
704
+ * valid TypeScript identifier. The resolver normalizes it. For inline enum properties the adapter
705
+ * already emits a PascalCase+suffix name, so resolution is typically a no-op.
706
+ *
707
+ * When `constCasing` is `'pascalCase'` and `typeSuffix` is empty, the const and the type
708
+ * resolve to the same name, which TypeScript merges into a single value+type declaration.
759
709
  */
760
- function getEnumNames({ node, enumType, enumTypeSuffix, resolver }) {
710
+ function getEnumNames({ node, enum: enumOptions, resolver }) {
761
711
  const resolved = resolver.default(node.name, "type");
762
712
  return {
763
- enumName: enumType === "asPascalConst" ? resolved : camelCase(node.name),
764
- typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved
713
+ enumName: enumOptions.constCasing === "pascalCase" ? resolved : camelCase(node.name),
714
+ typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolved
765
715
  };
766
716
  }
767
717
  /**
768
718
  * Renders the enum declaration(s) for a single named `EnumSchemaNode`.
769
719
  *
770
- * Depending on `enumType` this may emit:
771
- * - A runtime object (`asConst` / `asPascalConst`) plus a `typeof` type alias
720
+ * Depending on `enum.type` this may emit:
721
+ * - A runtime object (`asConst`) plus a `typeof` type alias
772
722
  * - A `const enum` or plain `enum` declaration (`constEnum` / `enum`)
773
723
  * - A union literal type alias (`literal`)
774
724
  *
775
725
  * The emitted `File.Source` nodes carry the resolved names so that the barrel
776
726
  * index picks up the correct export identifiers.
777
727
  */
778
- function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
728
+ function Enum({ node, enum: enumOptions, resolver }) {
779
729
  const { enumName, typeName } = getEnumNames({
780
730
  node,
781
- enumType,
782
- enumTypeSuffix,
731
+ enum: enumOptions,
783
732
  resolver
784
733
  });
785
734
  const [nameNode, typeNode] = createEnumDeclaration({
786
735
  name: enumName,
787
736
  typeName,
788
- enums: node.namedEnumValues?.map((v) => [trimQuotes(v.name.toString()), v.value]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
789
- type: enumType,
790
- enumKeyCasing
737
+ enums: node.namedEnumValues?.map((v) => [(0, _kubb_ast_utils.trimQuotes)(v.name.toString()), v.value]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [(0, _kubb_ast_utils.trimQuotes)(v.toString()), v]) ?? [],
738
+ type: enumOptions.type,
739
+ enumKeyCasing: enumOptions.keyCasing
791
740
  });
741
+ const namesMerge = !!nameNode && enumName === typeName;
792
742
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [nameNode && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
793
743
  name: enumName,
794
744
  isExportable: true,
@@ -797,15 +747,15 @@ function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
797
747
  children: _kubb_parser_ts.parserTs.print(nameNode)
798
748
  }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
799
749
  name: typeName,
800
- isIndexable: true,
801
- isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumType),
802
- isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumType),
750
+ isIndexable: !namesMerge,
751
+ isExportable: !namesMerge && ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumOptions.type),
752
+ isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumOptions.type),
803
753
  children: _kubb_parser_ts.parserTs.print(typeNode)
804
754
  })] });
805
755
  }
806
756
  //#endregion
807
757
  //#region src/components/Type.tsx
808
- function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
758
+ function Type({ name, node, printer, enum: enumOptions, resolver }) {
809
759
  const enumSchemaNodes = _kubb_core.ast.collect(node, { schema(n) {
810
760
  const enumNode = _kubb_core.ast.narrowSchema(n, _kubb_core.ast.schemaTypes.enum);
811
761
  if (enumNode?.name) return enumNode;
@@ -817,19 +767,16 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
817
767
  node,
818
768
  ...getEnumNames({
819
769
  node,
820
- enumType,
821
- enumTypeSuffix,
770
+ enum: enumOptions,
822
771
  resolver
823
772
  })
824
773
  };
825
774
  });
826
- const shouldExportEnums = enumType !== "inlineLiteral";
827
- const shouldExportType = enumType === "inlineLiteral" || enums.every((item) => item.typeName !== name);
775
+ const shouldExportEnums = enumOptions.type !== "inlineLiteral";
776
+ const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
828
777
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Enum, {
829
778
  node,
830
- enumType,
831
- enumTypeSuffix,
832
- enumKeyCasing,
779
+ enum: enumOptions,
833
780
  resolver
834
781
  }, node.name)), shouldExportType && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
835
782
  name,
@@ -905,26 +852,24 @@ function getOperationParameters(node, options = {}) {
905
852
  * shared default naming so every plugin groups output consistently:
906
853
  *
907
854
  * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
908
- * - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
855
+ * - other groups use the camelCased group (`pet store``petStore`).
909
856
  *
910
857
  * A user-provided `group.name` always wins over the default namer, so callers stay in
911
858
  * control of their output folders. Returns `null` when grouping is disabled, matching the
912
859
  * per-plugin convention.
913
860
  *
914
861
  * @param group - The user-supplied group option, or `undefined` to disable grouping.
915
- * @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
916
862
  *
917
863
  * @example
918
864
  * ```ts
919
- * createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
920
- * createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
865
+ * createGroupConfig(group) // shared across every plugin
921
866
  * ```
922
867
  */
923
- function createGroupConfig(group, options) {
868
+ function createGroupConfig(group) {
924
869
  if (!group) return null;
925
870
  const defaultName = (ctx) => {
926
871
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
927
- return `${camelCase(ctx.group)}${options.suffix}`;
872
+ return camelCase(ctx.group);
928
873
  };
929
874
  return {
930
875
  ...group,
@@ -946,13 +891,13 @@ function buildPropertyJSDocComments(schema) {
946
891
  const hasDescription = meta && "description" in meta && meta.description;
947
892
  const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
948
893
  return [
949
- hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
894
+ hasDescription ? `@description ${(0, _kubb_ast_utils.jsStringEscape)(meta.description)}` : null,
950
895
  ...formatComment,
951
896
  meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
952
897
  !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
953
898
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
954
899
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
955
- meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
900
+ meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? (0, _kubb_ast_utils.stringify)(meta.default) : meta.default}` : null,
956
901
  meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
957
902
  meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : null].filter(Boolean).join("") : null
958
903
  ].filter(Boolean);
@@ -1081,13 +1026,13 @@ function isNonNullable(value) {
1081
1026
  *
1082
1027
  * @example Raw type node (no `typeName`)
1083
1028
  * ```ts
1084
- * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral' })
1029
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' } })
1085
1030
  * const typeNode = printer.print(schemaNode) // ts.TypeNode
1086
1031
  * ```
1087
1032
  *
1088
1033
  * @example Full declaration (with `typeName`)
1089
1034
  * ```ts
1090
- * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral', typeName: 'MyType' })
1035
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' }, typeName: 'MyType' })
1091
1036
  * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
1092
1037
  * ```
1093
1038
  */
@@ -1121,16 +1066,16 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1121
1066
  time: dateOrStringNode,
1122
1067
  ref(node) {
1123
1068
  if (!node.name) return null;
1124
- const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
1125
- return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enumTypeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
1069
+ const refName = node.ref ? (0, _kubb_ast_utils.extractRefName)(node.ref) ?? node.name : node.name;
1070
+ return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
1126
1071
  },
1127
1072
  enum(node) {
1128
1073
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
1129
- if (this.options.enumType === "inlineLiteral" || !node.name) return createUnionDeclaration({
1074
+ if (this.options.enum.type === "inlineLiteral" || !node.name) return createUnionDeclaration({
1130
1075
  withParentheses: true,
1131
1076
  nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
1132
1077
  }) ?? void 0;
1133
- return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix ? this.options.resolver.resolveEnumKeyName(node, this.options.enumTypeSuffix) : this.options.resolver.default(node.name, "type"), void 0);
1078
+ return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.resolveEnumKeyName(node, this.options.enum.typeSuffix) : this.options.resolver.default(node.name, "type"), void 0);
1134
1079
  },
1135
1080
  union(node) {
1136
1081
  const members = node.members ?? [];
@@ -1234,15 +1179,14 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1234
1179
  */
1235
1180
  const typeGenerator = (0, _kubb_core.defineGenerator)({
1236
1181
  name: "typescript",
1237
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1182
+ renderer: _kubb_renderer_jsx.jsxRenderer,
1238
1183
  schema(node, ctx) {
1239
- const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
1184
+ const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
1240
1185
  const { adapter, config, resolver, root } = ctx;
1241
1186
  if (!node.name) return;
1242
- const mode = ctx.getMode(output);
1243
1187
  const enumSchemaNames = new Set(ctx.meta.enumNames);
1244
1188
  function resolveImportName(schemaName) {
1245
- if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
1189
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix);
1246
1190
  return resolver.resolveTypeName(schemaName);
1247
1191
  }
1248
1192
  const imports = adapter.getImports(node, (schemaName) => ({
@@ -1258,7 +1202,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1258
1202
  }));
1259
1203
  const isEnumSchema = !!_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum);
1260
1204
  const meta = {
1261
- name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolver.resolveTypeName(node.name),
1205
+ name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolver.resolveTypeName(node.name),
1262
1206
  file: resolver.resolveFile({
1263
1207
  name: node.name,
1264
1208
  extname: ".ts"
@@ -1271,8 +1215,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1271
1215
  const schemaPrinter = printerTs({
1272
1216
  optionalType,
1273
1217
  arrayType,
1274
- enumType,
1275
- enumTypeSuffix,
1218
+ enum: enumOptions,
1276
1219
  name: meta.name,
1277
1220
  syntaxType,
1278
1221
  description: node.description,
@@ -1300,7 +1243,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1300
1243
  baseName: meta.file.baseName
1301
1244
  }
1302
1245
  }),
1303
- children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1246
+ children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1304
1247
  root: meta.file.path,
1305
1248
  path: imp.path,
1306
1249
  name: imp.name,
@@ -1312,18 +1255,15 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1312
1255
  ].join("-"))), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Type, {
1313
1256
  name: meta.name,
1314
1257
  node,
1315
- enumType,
1316
- enumTypeSuffix,
1317
- enumKeyCasing,
1258
+ enum: enumOptions,
1318
1259
  resolver,
1319
1260
  printer: schemaPrinter
1320
1261
  })]
1321
1262
  });
1322
1263
  },
1323
1264
  operation(node, ctx) {
1324
- const { enumType, enumTypeSuffix, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1265
+ const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1325
1266
  const { adapter, config, resolver, root } = ctx;
1326
- const mode = ctx.getMode(output);
1327
1267
  const params = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
1328
1268
  const meta = { file: resolver.resolveFile({
1329
1269
  name: node.operationId,
@@ -1337,7 +1277,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1337
1277
  }) };
1338
1278
  const enumSchemaNames = new Set(ctx.meta.enumNames);
1339
1279
  function resolveImportName(schemaName) {
1340
- if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
1280
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix);
1341
1281
  return resolver.resolveTypeName(schemaName);
1342
1282
  }
1343
1283
  function renderSchemaType({ schema, name, keysToOmit }) {
@@ -1356,8 +1296,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1356
1296
  const schemaPrinter = printerTs({
1357
1297
  optionalType,
1358
1298
  arrayType,
1359
- enumType,
1360
- enumTypeSuffix,
1299
+ enum: enumOptions,
1361
1300
  name,
1362
1301
  syntaxType,
1363
1302
  description: schema.description,
@@ -1366,7 +1305,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1366
1305
  enumSchemaNames,
1367
1306
  nodes: printer?.nodes
1368
1307
  });
1369
- 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, {
1308
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1370
1309
  root: meta.file.path,
1371
1310
  path: imp.path,
1372
1311
  name: imp.name,
@@ -1378,9 +1317,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1378
1317
  ].join("-"))), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Type, {
1379
1318
  name,
1380
1319
  node: schema,
1381
- enumType,
1382
- enumTypeSuffix,
1383
- enumKeyCasing,
1320
+ enum: enumOptions,
1384
1321
  resolver,
1385
1322
  printer: schemaPrinter
1386
1323
  })] });
@@ -1511,7 +1448,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1511
1448
  * casing/file-layout rules.
1512
1449
  *
1513
1450
  * The `default` method is supplied by `defineResolver`. It uses PascalCase for
1514
- * type names and PascalCase-with-isFile for files.
1451
+ * type names and PascalCase file paths (dotted names become `/`-joined) for files.
1515
1452
  *
1516
1453
  * @example Resolve a type and file name
1517
1454
  * ```ts
@@ -1527,15 +1464,15 @@ const resolverTs = (0, _kubb_core.defineResolver)(() => {
1527
1464
  name: "default",
1528
1465
  pluginName: "plugin-ts",
1529
1466
  default(name, type) {
1530
- const resolved = pascalCase(name, { isFile: type === "file" });
1531
- return type === "file" ? resolved : ensureValidVarName(resolved);
1467
+ if (type === "file") return toFilePath(name, pascalCase);
1468
+ return ensureValidVarName(pascalCase(name));
1532
1469
  },
1533
1470
  resolveTypeName(name) {
1534
1471
  return ensureValidVarName(pascalCase(name));
1535
1472
  },
1536
1473
  resolvePathName(name, type) {
1537
- const resolved = pascalCase(name, { isFile: type === "file" });
1538
- return type === "file" ? resolved : ensureValidVarName(resolved);
1474
+ if (type === "file") return toFilePath(name, pascalCase);
1475
+ return ensureValidVarName(pascalCase(name));
1539
1476
  },
1540
1477
  resolveParamName(node, param) {
1541
1478
  return this.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
@@ -1593,7 +1530,7 @@ const pluginTsName = "plugin-ts";
1593
1530
  * plugins: [
1594
1531
  * pluginTs({
1595
1532
  * output: { path: './types' },
1596
- * enumType: 'asConst',
1533
+ * enum: { type: 'asConst' },
1597
1534
  * optionalType: 'questionTokenAndUndefined',
1598
1535
  * }),
1599
1536
  * ],
@@ -1603,9 +1540,15 @@ const pluginTsName = "plugin-ts";
1603
1540
  const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1604
1541
  const { output = {
1605
1542
  path: "types",
1606
- barrelType: "named"
1607
- }, group, exclude = [], include, override = [], enumType = "asConst", enumTypeSuffix = "Key", enumKeyCasing = "none", optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1608
- const groupConfig = createGroupConfig(group, { suffix: "Controller" });
1543
+ barrel: { type: "named" }
1544
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1545
+ const groupConfig = createGroupConfig(group);
1546
+ const resolvedEnum = {
1547
+ type: enumOptions.type ?? "asConst",
1548
+ constCasing: enumOptions.constCasing ?? "camelCase",
1549
+ typeSuffix: enumOptions.typeSuffix ?? "Key",
1550
+ keyCasing: enumOptions.keyCasing ?? "none"
1551
+ };
1609
1552
  return {
1610
1553
  name: pluginTsName,
1611
1554
  options,
@@ -1618,9 +1561,7 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1618
1561
  optionalType,
1619
1562
  group: groupConfig,
1620
1563
  arrayType,
1621
- enumType,
1622
- enumTypeSuffix,
1623
- enumKeyCasing,
1564
+ enum: resolvedEnum,
1624
1565
  syntaxType,
1625
1566
  paramsCasing,
1626
1567
  printer