@kubb/plugin-ts 5.0.0-beta.3 → 5.0.0-beta.30

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
@@ -5,6 +5,10 @@ Object.defineProperties(exports, {
5
5
  //#region \0rolldown/runtime.js
6
6
  var __create = Object.create;
7
7
  var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", {
9
+ value,
10
+ configurable: true
11
+ });
8
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
13
  var __getOwnPropNames = Object.getOwnPropertyNames;
10
14
  var __getProtoOf = Object.getPrototypeOf;
@@ -167,6 +171,129 @@ function stringify(value) {
167
171
  return JSON.stringify(trimQuotes(value.toString()));
168
172
  }
169
173
  //#endregion
174
+ //#region ../../internals/utils/src/reserved.ts
175
+ /**
176
+ * JavaScript and Java reserved words.
177
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
178
+ */
179
+ const reservedWords = new Set([
180
+ "abstract",
181
+ "arguments",
182
+ "boolean",
183
+ "break",
184
+ "byte",
185
+ "case",
186
+ "catch",
187
+ "char",
188
+ "class",
189
+ "const",
190
+ "continue",
191
+ "debugger",
192
+ "default",
193
+ "delete",
194
+ "do",
195
+ "double",
196
+ "else",
197
+ "enum",
198
+ "eval",
199
+ "export",
200
+ "extends",
201
+ "false",
202
+ "final",
203
+ "finally",
204
+ "float",
205
+ "for",
206
+ "function",
207
+ "goto",
208
+ "if",
209
+ "implements",
210
+ "import",
211
+ "in",
212
+ "instanceof",
213
+ "int",
214
+ "interface",
215
+ "let",
216
+ "long",
217
+ "native",
218
+ "new",
219
+ "null",
220
+ "package",
221
+ "private",
222
+ "protected",
223
+ "public",
224
+ "return",
225
+ "short",
226
+ "static",
227
+ "super",
228
+ "switch",
229
+ "synchronized",
230
+ "this",
231
+ "throw",
232
+ "throws",
233
+ "transient",
234
+ "true",
235
+ "try",
236
+ "typeof",
237
+ "var",
238
+ "void",
239
+ "volatile",
240
+ "while",
241
+ "with",
242
+ "yield",
243
+ "Array",
244
+ "Date",
245
+ "hasOwnProperty",
246
+ "Infinity",
247
+ "isFinite",
248
+ "isNaN",
249
+ "isPrototypeOf",
250
+ "length",
251
+ "Math",
252
+ "name",
253
+ "NaN",
254
+ "Number",
255
+ "Object",
256
+ "prototype",
257
+ "String",
258
+ "toString",
259
+ "undefined",
260
+ "valueOf"
261
+ ]);
262
+ /**
263
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
264
+ *
265
+ * @example
266
+ * ```ts
267
+ * isValidVarName('status') // true
268
+ * isValidVarName('class') // false (reserved word)
269
+ * isValidVarName('42foo') // false (starts with digit)
270
+ * ```
271
+ */
272
+ function isValidVarName(name) {
273
+ if (!name || reservedWords.has(name)) return false;
274
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
275
+ }
276
+ /**
277
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
278
+ * otherwise prefixes it with `_` so the result is a valid identifier.
279
+ *
280
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
281
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
282
+ * variable, type, or function names.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * ensureValidVarName('409') // '_409'
287
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
288
+ * ensureValidVarName('Pet') // 'Pet'
289
+ * ensureValidVarName('class') // '_class'
290
+ * ```
291
+ */
292
+ function ensureValidVarName(name) {
293
+ if (!name || isValidVarName(name)) return name;
294
+ return `_${name}`;
295
+ }
296
+ //#endregion
170
297
  //#region src/constants.ts
171
298
  /**
172
299
  * `optionalType` values that cause a property's type to include `| undefined`.
@@ -229,6 +356,10 @@ const syntaxKind = {
229
356
  literalType: SyntaxKind.LiteralType,
230
357
  stringLiteral: SyntaxKind.StringLiteral
231
358
  };
359
+ function isNonNullable$1(value) {
360
+ return value !== null && value !== void 0;
361
+ }
362
+ __name(isNonNullable$1, "isNonNullable");
232
363
  function isValidIdentifier(str) {
233
364
  if (!str.length || str.trim() !== str) return false;
234
365
  const node = typescript.default.parseIsolatedEntityName(str, typescript.default.ScriptTarget.Latest);
@@ -298,7 +429,7 @@ function createUnionDeclaration({ nodes, withParentheses }) {
298
429
  * Supports optional markers, readonly modifiers, and type annotations.
299
430
  */
300
431
  function createPropertySignature({ readOnly, modifiers = [], name, questionToken, type }) {
301
- return factory.createPropertySignature([...modifiers, readOnly ? factory.createToken(typescript.default.SyntaxKind.ReadonlyKeyword) : void 0].filter(Boolean), propertyName(name), createQuestionToken(questionToken), type);
432
+ return factory.createPropertySignature([...modifiers, readOnly ? factory.createToken(typescript.default.SyntaxKind.ReadonlyKeyword) : void 0].filter((modifier) => modifier !== void 0), propertyName(name), createQuestionToken(questionToken), type);
302
433
  }
303
434
  /**
304
435
  * Creates a function parameter declaration with optional markers, rest parameters, and type annotations.
@@ -396,8 +527,8 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
396
527
  }
397
528
  if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
398
529
  if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
399
- }).filter(Boolean)))];
400
- if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter(Boolean), factory.createIdentifier(typeName), enums.map(([key, value]) => {
530
+ }).filter((node) => node !== void 0)))];
531
+ if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
401
532
  let initializer = factory.createStringLiteral(value?.toString());
402
533
  if (Number.parseInt(value.toString(), 10) === value && (0, remeda.isNumber)(Number.parseInt(value.toString(), 10))) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
403
534
  else initializer = factory.createNumericLiteral(value);
@@ -410,7 +541,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
410
541
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
411
542
  return factory.createEnumMember(propertyName(casingKey), initializer);
412
543
  }
413
- }).filter(Boolean))];
544
+ }).filter((member) => member !== void 0))];
414
545
  const identifierName = name;
415
546
  if (enums.length === 0) return [void 0, factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createKeywordTypeNode(typescript.default.SyntaxKind.NeverKeyword))];
416
547
  return [factory.createVariableStatement([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value]) => {
@@ -422,7 +553,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
422
553
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
423
554
  return factory.createPropertyAssignment(propertyName(casingKey), initializer);
424
555
  }
425
- }).filter(Boolean), true), factory.createTypeReferenceNode(factory.createIdentifier("const"), void 0)))], typescript.default.NodeFlags.Const)), factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0)), factory.createTypeOperatorNode(typescript.default.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0))))];
556
+ }).filter((property) => property !== void 0), true), factory.createTypeReferenceNode(factory.createIdentifier("const"), void 0)))], typescript.default.NodeFlags.Const)), factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0)), factory.createTypeOperatorNode(typescript.default.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0))))];
426
557
  }
427
558
  /**
428
559
  * Creates a TypeScript `Omit<T, Keys>` type reference node.
@@ -562,14 +693,14 @@ function dateOrStringNode(node) {
562
693
  * Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
563
694
  */
564
695
  function buildMemberNodes(members, print) {
565
- return (members ?? []).map(print).filter(Boolean);
696
+ return (members ?? []).map(print).filter(isNonNullable$1);
566
697
  }
567
698
  /**
568
699
  * Builds a TypeScript tuple type node from an array schema's `items`,
569
700
  * applying min/max slice and optional/rest element rules.
570
701
  */
571
702
  function buildTupleNode(node, print) {
572
- let items = (node.items ?? []).map(print).filter(Boolean);
703
+ let items = (node.items ?? []).map(print).filter(isNonNullable$1);
573
704
  const restNode = node.rest ? print(node.rest) ?? void 0 : void 0;
574
705
  const { min, max } = node;
575
706
  if (max !== void 0) {
@@ -656,13 +787,13 @@ function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
656
787
  isExportable: true,
657
788
  isIndexable: true,
658
789
  isTypeOnly: false,
659
- children: (0, _kubb_parser_ts.safePrint)(nameNode)
790
+ children: _kubb_parser_ts.parserTs.print(nameNode)
660
791
  }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
661
792
  name: typeName,
662
793
  isIndexable: true,
663
794
  isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumType),
664
795
  isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumType),
665
- children: (0, _kubb_parser_ts.safePrint)(typeNode)
796
+ children: _kubb_parser_ts.parserTs.print(typeNode)
666
797
  })] });
667
798
  }
668
799
  //#endregion
@@ -702,6 +833,17 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
702
833
  })] });
703
834
  }
704
835
  //#endregion
836
+ //#region ../../internals/shared/src/operation.ts
837
+ function getOperationParameters(node, options = {}) {
838
+ const params = _kubb_core.ast.caseParams(node.parameters, options.paramsCasing);
839
+ return {
840
+ path: params.filter((param) => param.in === "path"),
841
+ query: params.filter((param) => param.in === "query"),
842
+ header: params.filter((param) => param.in === "header"),
843
+ cookie: params.filter((param) => param.in === "cookie")
844
+ };
845
+ }
846
+ //#endregion
705
847
  //#region src/utils.ts
706
848
  /**
707
849
  * Collects JSDoc annotation strings for a schema node.
@@ -713,15 +855,18 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
713
855
  function buildPropertyJSDocComments(schema) {
714
856
  const meta = _kubb_core.ast.syncSchemaRef(schema);
715
857
  const isArray = meta?.primitive === "array";
858
+ const hasDescription = meta && "description" in meta && meta.description;
859
+ const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
716
860
  return [
717
- meta && "description" in meta && meta.description ? `@description ${jsStringEscape(meta.description)}` : void 0,
718
- meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : void 0,
719
- !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : void 0,
720
- !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : void 0,
721
- meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : void 0,
722
- meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : void 0,
723
- meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : void 0,
724
- meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : void 0].filter(Boolean).join("") : void 0
861
+ hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
862
+ ...formatComment,
863
+ meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
864
+ !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
865
+ !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
866
+ meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
867
+ meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
868
+ meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
869
+ meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : null].filter(Boolean).join("") : null
725
870
  ].filter(Boolean);
726
871
  }
727
872
  function buildParams(node, { params, resolver }) {
@@ -738,9 +883,7 @@ function buildParams(node, { params, resolver }) {
738
883
  });
739
884
  }
740
885
  function buildData(node, { resolver }) {
741
- const pathParams = node.parameters.filter((p) => p.in === "path");
742
- const queryParams = node.parameters.filter((p) => p.in === "query");
743
- const headerParams = node.parameters.filter((p) => p.in === "header");
886
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
744
887
  return _kubb_core.ast.createSchema({
745
888
  type: "object",
746
889
  deprecated: node.deprecated,
@@ -822,7 +965,7 @@ function buildResponses(node, { resolver }) {
822
965
  });
823
966
  }
824
967
  function buildResponseUnion(node, { resolver }) {
825
- const responsesWithSchema = node.responses.filter((res) => res.schema);
968
+ const responsesWithSchema = node.responses.filter((res) => res.content?.[0]?.schema);
826
969
  if (responsesWithSchema.length === 0) return null;
827
970
  return _kubb_core.ast.createSchema({
828
971
  type: "union",
@@ -834,6 +977,9 @@ function buildResponseUnion(node, { resolver }) {
834
977
  }
835
978
  //#endregion
836
979
  //#region src/printers/printerTs.ts
980
+ function isNonNullable(value) {
981
+ return value !== null && value !== void 0;
982
+ }
837
983
  /**
838
984
  * TypeScript type printer built with `definePrinter`.
839
985
  *
@@ -886,7 +1032,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
886
1032
  date: dateOrStringNode,
887
1033
  time: dateOrStringNode,
888
1034
  ref(node) {
889
- if (!node.name) return;
1035
+ if (!node.name) return null;
890
1036
  const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
891
1037
  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);
892
1038
  },
@@ -894,7 +1040,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
894
1040
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
895
1041
  if (this.options.enumType === "inlineLiteral" || !node.name) return createUnionDeclaration({
896
1042
  withParentheses: true,
897
- nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(Boolean)
1043
+ nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
898
1044
  }) ?? void 0;
899
1045
  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);
900
1046
  },
@@ -912,7 +1058,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
912
1058
  withParentheses: true
913
1059
  });
914
1060
  return this.transform(m);
915
- }).filter(Boolean)
1061
+ }).filter(isNonNullable)
916
1062
  }) ?? void 0;
917
1063
  return createUnionDeclaration({
918
1064
  withParentheses: true,
@@ -923,16 +1069,16 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
923
1069
  return createIntersectionDeclaration({
924
1070
  withParentheses: true,
925
1071
  nodes: buildMemberNodes(node.members, this.transform)
926
- }) ?? void 0;
1072
+ }) ?? null;
927
1073
  },
928
1074
  array(node) {
929
1075
  return createArrayDeclaration({
930
- nodes: (node.items ?? []).map((item) => this.transform(item)).filter(Boolean),
1076
+ nodes: (node.items ?? []).map((item) => this.transform(item)).filter(isNonNullable),
931
1077
  arrayType: this.options.arrayType
932
- }) ?? void 0;
1078
+ }) ?? null;
933
1079
  },
934
1080
  tuple(node) {
935
- return buildTupleNode(node, this.transform);
1081
+ return buildTupleNode(node, this.transform) ?? null;
936
1082
  },
937
1083
  object(node) {
938
1084
  const { transform, options } = this;
@@ -959,46 +1105,67 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
959
1105
  },
960
1106
  print(node) {
961
1107
  const { name, syntaxType = "type", description, keysToOmit } = this.options;
962
- let base = this.transform(node);
963
- if (!base) return null;
1108
+ const transformed = this.transform(node);
1109
+ if (!transformed) return null;
964
1110
  const meta = _kubb_core.ast.syncSchemaRef(node);
965
1111
  if (!name) {
966
- if (meta.nullable) base = createUnionDeclaration({ nodes: [base, keywordTypeNodes.null] });
967
- if ((meta.nullish || meta.optional) && addsUndefined) base = createUnionDeclaration({ nodes: [base, keywordTypeNodes.undefined] });
968
- return (0, _kubb_parser_ts.safePrint)(base);
1112
+ const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
1113
+ const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
1114
+ return _kubb_parser_ts.parserTs.print(result);
969
1115
  }
970
- let inner = keysToOmit?.length ? createOmitDeclaration({
971
- keys: keysToOmit,
972
- type: base,
973
- nonNullable: true
974
- }) : base;
975
- if (meta.nullable) inner = createUnionDeclaration({ nodes: [inner, keywordTypeNodes.null] });
976
- if (meta.nullish || meta.optional) inner = createUnionDeclaration({ nodes: [inner, keywordTypeNodes.undefined] });
977
- const useTypeGeneration = syntaxType === "type" || inner.kind === syntaxKind.union || !!keysToOmit?.length;
978
- return (0, _kubb_parser_ts.safePrint)(createTypeDeclaration({
1116
+ const inner = (() => {
1117
+ const omitted = keysToOmit?.length ? createOmitDeclaration({
1118
+ keys: keysToOmit,
1119
+ type: transformed,
1120
+ nonNullable: true
1121
+ }) : transformed;
1122
+ const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [omitted, keywordTypeNodes.null] }) : omitted;
1123
+ return meta.nullish || meta.optional ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
1124
+ })();
1125
+ const typeNode = createTypeDeclaration({
979
1126
  name,
980
1127
  isExportable: true,
981
1128
  type: inner,
982
- syntax: useTypeGeneration ? "type" : "interface",
1129
+ syntax: syntaxType === "type" || inner.kind === syntaxKind.union || !!keysToOmit?.length ? "type" : "interface",
983
1130
  comments: buildPropertyJSDocComments({
984
1131
  ...meta,
985
1132
  description
986
1133
  })
987
- }));
1134
+ });
1135
+ return _kubb_parser_ts.parserTs.print(typeNode);
988
1136
  }
989
1137
  };
990
1138
  });
991
1139
  //#endregion
992
1140
  //#region src/generators/typeGenerator.tsx
1141
+ function getContentTypeSuffix(contentType) {
1142
+ const baseType = contentType.split(";")[0].trim();
1143
+ if (baseType === "application/json") return "Json";
1144
+ if (baseType === "multipart/form-data") return "FormData";
1145
+ if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
1146
+ const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
1147
+ if (parts.length === 0) return "Unknown";
1148
+ return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1149
+ }
1150
+ function getPerContentTypeName(dataName, suffix) {
1151
+ if (dataName.endsWith("Data")) return suffix.endsWith("Data") ? dataName.slice(0, -4) + suffix : `${dataName.slice(0, -4)}${suffix}Data`;
1152
+ return dataName + suffix;
1153
+ }
1154
+ /**
1155
+ * Built-in generator for `@kubb/plugin-ts`. Emits one TypeScript file per
1156
+ * schema in the spec plus per-operation request, response, and parameter
1157
+ * types. Drop-replace with a custom `Generator<PluginTs>` to change how
1158
+ * TypeScript output is produced.
1159
+ */
993
1160
  const typeGenerator = (0, _kubb_core.defineGenerator)({
994
1161
  name: "typescript",
995
- renderer: _kubb_renderer_jsx.jsxRenderer,
1162
+ renderer: _kubb_renderer_jsx.jsxRendererSync,
996
1163
  schema(node, ctx) {
997
1164
  const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
998
1165
  const { adapter, config, resolver, root } = ctx;
999
1166
  if (!node.name) return;
1000
1167
  const mode = ctx.getMode(output);
1001
- const enumSchemaNames = new Set((adapter.inputNode?.schemas ?? []).filter((s) => _kubb_core.ast.narrowSchema(s, _kubb_core.ast.schemaTypes.enum) && s.name).map((s) => s.name));
1168
+ const enumSchemaNames = new Set(ctx.meta.enumNames);
1002
1169
  function resolveImportName(schemaName) {
1003
1170
  if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
1004
1171
  return resolver.resolveTypeName(schemaName);
@@ -1011,7 +1178,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1011
1178
  }, {
1012
1179
  root,
1013
1180
  output,
1014
- group
1181
+ group: group ?? void 0
1015
1182
  }).path
1016
1183
  }));
1017
1184
  const isEnumSchema = !!_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum);
@@ -1023,7 +1190,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1023
1190
  }, {
1024
1191
  root,
1025
1192
  output,
1026
- group
1193
+ group: group ?? void 0
1027
1194
  })
1028
1195
  };
1029
1196
  const schemaPrinter = printerTs({
@@ -1042,13 +1209,21 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1042
1209
  baseName: meta.file.baseName,
1043
1210
  path: meta.file.path,
1044
1211
  meta: meta.file.meta,
1045
- banner: resolver.resolveBanner(adapter.inputNode, {
1212
+ banner: resolver.resolveBanner(ctx.meta, {
1046
1213
  output,
1047
- config
1214
+ config,
1215
+ file: {
1216
+ path: meta.file.path,
1217
+ baseName: meta.file.baseName
1218
+ }
1048
1219
  }),
1049
- footer: resolver.resolveFooter(adapter.inputNode, {
1220
+ footer: resolver.resolveFooter(ctx.meta, {
1050
1221
  output,
1051
- config
1222
+ config,
1223
+ file: {
1224
+ path: meta.file.path,
1225
+ baseName: meta.file.baseName
1226
+ }
1052
1227
  }),
1053
1228
  children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1054
1229
  root: meta.file.path,
@@ -1083,9 +1258,9 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1083
1258
  }, {
1084
1259
  root,
1085
1260
  output,
1086
- group
1261
+ group: group ?? void 0
1087
1262
  }) };
1088
- const enumSchemaNames = new Set((adapter.inputNode?.schemas ?? []).filter((s) => _kubb_core.ast.narrowSchema(s, _kubb_core.ast.schemaTypes.enum) && s.name).map((s) => s.name));
1263
+ const enumSchemaNames = new Set(ctx.meta.enumNames);
1089
1264
  function resolveImportName(schemaName) {
1090
1265
  if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
1091
1266
  return resolver.resolveTypeName(schemaName);
@@ -1100,7 +1275,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1100
1275
  }, {
1101
1276
  root,
1102
1277
  output,
1103
- group
1278
+ group: group ?? void 0
1104
1279
  }).path
1105
1280
  }));
1106
1281
  const schemaPrinter = printerTs({
@@ -1139,18 +1314,58 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1139
1314
  schema: param.schema,
1140
1315
  name: resolver.resolveParamName(node, param)
1141
1316
  }));
1142
- const requestType = node.requestBody?.content?.[0]?.schema ? renderSchemaType({
1143
- schema: {
1144
- ...node.requestBody.content[0].schema,
1145
- description: node.requestBody.description ?? node.requestBody.content[0].schema.description
1146
- },
1147
- name: resolver.resolveDataName(node),
1148
- keysToOmit: node.requestBody.content[0].keysToOmit
1149
- }) : null;
1317
+ const requestBodyContent = node.requestBody?.content ?? [];
1318
+ function buildRequestType() {
1319
+ if (requestBodyContent.length === 0) return null;
1320
+ if (requestBodyContent.length === 1) {
1321
+ const entry = requestBodyContent[0];
1322
+ if (!entry.schema) return null;
1323
+ return renderSchemaType({
1324
+ schema: {
1325
+ ...entry.schema,
1326
+ description: node.requestBody.description ?? entry.schema.description
1327
+ },
1328
+ name: resolver.resolveDataName(node),
1329
+ keysToOmit: entry.keysToOmit
1330
+ });
1331
+ }
1332
+ const dataName = resolver.resolveDataName(node);
1333
+ const usedNames = /* @__PURE__ */ new Set();
1334
+ const individualItems = requestBodyContent.filter((entry) => entry.schema).map((entry) => {
1335
+ const baseSuffix = getContentTypeSuffix(entry.contentType);
1336
+ let individualName = getPerContentTypeName(dataName, baseSuffix);
1337
+ let counter = 2;
1338
+ while (usedNames.has(individualName)) individualName = getPerContentTypeName(dataName, `${baseSuffix}${counter++}`);
1339
+ usedNames.add(individualName);
1340
+ return {
1341
+ name: individualName,
1342
+ rendered: renderSchemaType({
1343
+ schema: {
1344
+ ...entry.schema,
1345
+ description: node.requestBody.description ?? entry.schema.description
1346
+ },
1347
+ name: individualName,
1348
+ keysToOmit: entry.keysToOmit
1349
+ })
1350
+ };
1351
+ });
1352
+ const unionType = renderSchemaType({
1353
+ schema: _kubb_core.ast.createSchema({
1354
+ type: "union",
1355
+ members: individualItems.map((item) => _kubb_core.ast.createSchema({
1356
+ type: "ref",
1357
+ name: item.name
1358
+ }))
1359
+ }),
1360
+ name: dataName
1361
+ });
1362
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [individualItems.map((item) => item.rendered), unionType] });
1363
+ }
1364
+ const requestType = buildRequestType();
1150
1365
  const responseTypes = node.responses.map((res) => renderSchemaType({
1151
- schema: res.schema,
1366
+ schema: res.content?.[0]?.schema ?? null,
1152
1367
  name: resolver.resolveResponseStatusName(node, res.statusCode),
1153
- keysToOmit: res.keysToOmit
1368
+ keysToOmit: res.content?.[0]?.keysToOmit
1154
1369
  }));
1155
1370
  const dataType = renderSchemaType({
1156
1371
  schema: buildData({
@@ -1163,11 +1378,11 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1163
1378
  schema: buildResponses(node, { resolver }),
1164
1379
  name: resolver.resolveResponsesName(node)
1165
1380
  });
1166
- const responseType = (() => {
1167
- if (!node.responses.some((res) => res.schema)) return null;
1381
+ function buildResponseType() {
1382
+ if (!node.responses.some((res) => res.content?.[0]?.schema)) return null;
1168
1383
  const responseName = resolver.resolveResponseName(node);
1169
- const responsesWithSchema = node.responses.filter((res) => res.schema);
1170
- if (new Set(responsesWithSchema.flatMap((res) => res.schema ? adapter.getImports(res.schema, (schemaName) => ({
1384
+ const responsesWithSchema = node.responses.filter((res) => res.content?.[0]?.schema);
1385
+ if (new Set(responsesWithSchema.flatMap((res) => res.content?.[0]?.schema ? adapter.getImports(res.content[0].schema, (schemaName) => ({
1171
1386
  name: resolveImportName(schemaName),
1172
1387
  path: ""
1173
1388
  })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseName)) return null;
@@ -1178,18 +1393,27 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1178
1393
  },
1179
1394
  name: responseName
1180
1395
  });
1181
- })();
1396
+ }
1397
+ const responseType = buildResponseType();
1182
1398
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1183
1399
  baseName: meta.file.baseName,
1184
1400
  path: meta.file.path,
1185
1401
  meta: meta.file.meta,
1186
- banner: resolver.resolveBanner(adapter.inputNode, {
1402
+ banner: resolver.resolveBanner(ctx.meta, {
1187
1403
  output,
1188
- config
1404
+ config,
1405
+ file: {
1406
+ path: meta.file.path,
1407
+ baseName: meta.file.baseName
1408
+ }
1189
1409
  }),
1190
- footer: resolver.resolveFooter(adapter.inputNode, {
1410
+ footer: resolver.resolveFooter(ctx.meta, {
1191
1411
  output,
1192
- config
1412
+ config,
1413
+ file: {
1414
+ path: meta.file.path,
1415
+ baseName: meta.file.baseName
1416
+ }
1193
1417
  }),
1194
1418
  children: [
1195
1419
  paramTypes,
@@ -1205,86 +1429,98 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1205
1429
  //#endregion
1206
1430
  //#region src/resolvers/resolverTs.ts
1207
1431
  /**
1208
- * Resolver for `@kubb/plugin-ts` that provides the default naming and path-resolution
1209
- * helpers used by the plugin. Import this in other plugins to resolve the exact names and
1210
- * paths that `plugin-ts` generates without hardcoding the conventions.
1432
+ * Default resolver used by `@kubb/plugin-ts`. Decides the names and file paths
1433
+ * for every generated TypeScript type. Import this in other plugins that need
1434
+ * to reference the exact names `plugin-ts` produces without duplicating the
1435
+ * casing/file-layout rules.
1211
1436
  *
1212
- * The `default` method is automatically injected by `defineResolver` it uses `camelCase`
1213
- * for identifiers/files and `pascalCase` for type names.
1437
+ * The `default` method is supplied by `defineResolver`. It uses PascalCase for
1438
+ * type names and PascalCase-with-isFile for files.
1214
1439
  *
1215
- * @example
1440
+ * @example Resolve a type and file name
1216
1441
  * ```ts
1217
- * import { resolver } from '@kubb/plugin-ts'
1442
+ * import { resolverTs } from '@kubb/plugin-ts'
1218
1443
  *
1219
- * resolver.default('list pets', 'type') // 'ListPets'
1220
- * resolver.resolveName('list pets status 200') // 'ListPetsStatus200'
1221
- * resolver.resolvePathName('list pets', 'file') // 'listPets'
1444
+ * resolverTs.default('list pets', 'type') // 'ListPets'
1445
+ * resolverTs.resolvePathName('list pets', 'file') // 'ListPets'
1446
+ * resolverTs.resolveResponseStatusName(node, 200) // 'ListPetsStatus200'
1222
1447
  * ```
1223
1448
  */
1224
- const resolverTs = (0, _kubb_core.defineResolver)((ctx) => {
1449
+ const resolverTs = (0, _kubb_core.defineResolver)(() => {
1225
1450
  return {
1226
1451
  name: "default",
1227
1452
  pluginName: "plugin-ts",
1228
1453
  default(name, type) {
1229
- return pascalCase(name, { isFile: type === "file" });
1454
+ const resolved = pascalCase(name, { isFile: type === "file" });
1455
+ return type === "file" ? resolved : ensureValidVarName(resolved);
1230
1456
  },
1231
1457
  resolveTypeName(name) {
1232
- return pascalCase(name);
1458
+ return ensureValidVarName(pascalCase(name));
1233
1459
  },
1234
1460
  resolvePathName(name, type) {
1235
- return pascalCase(name, { isFile: type === "file" });
1461
+ const resolved = pascalCase(name, { isFile: type === "file" });
1462
+ return type === "file" ? resolved : ensureValidVarName(resolved);
1236
1463
  },
1237
1464
  resolveParamName(node, param) {
1238
- return ctx.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
1465
+ return this.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
1239
1466
  },
1240
1467
  resolveResponseStatusName(node, statusCode) {
1241
- return ctx.resolveTypeName(`${node.operationId} Status ${statusCode}`);
1468
+ return this.resolveTypeName(`${node.operationId} Status ${statusCode}`);
1242
1469
  },
1243
1470
  resolveDataName(node) {
1244
- return ctx.resolveTypeName(`${node.operationId} Data`);
1471
+ return this.resolveTypeName(`${node.operationId} Data`);
1245
1472
  },
1246
1473
  resolveRequestConfigName(node) {
1247
- return ctx.resolveTypeName(`${node.operationId} RequestConfig`);
1474
+ return this.resolveTypeName(`${node.operationId} RequestConfig`);
1248
1475
  },
1249
1476
  resolveResponsesName(node) {
1250
- return ctx.resolveTypeName(`${node.operationId} Responses`);
1477
+ return this.resolveTypeName(`${node.operationId} Responses`);
1251
1478
  },
1252
1479
  resolveResponseName(node) {
1253
- return ctx.resolveTypeName(`${node.operationId} Response`);
1480
+ return this.resolveTypeName(`${node.operationId} Response`);
1254
1481
  },
1255
1482
  resolveEnumKeyName(node, enumTypeSuffix = "key") {
1256
- return `${ctx.resolveTypeName(node.name ?? "")}${enumTypeSuffix}`;
1483
+ return `${this.resolveTypeName(node.name ?? "")}${enumTypeSuffix}`;
1257
1484
  },
1258
1485
  resolvePathParamsName(node, param) {
1259
- return ctx.resolveParamName(node, param);
1486
+ return this.resolveParamName(node, param);
1260
1487
  },
1261
1488
  resolveQueryParamsName(node, param) {
1262
- return ctx.resolveParamName(node, param);
1489
+ return this.resolveParamName(node, param);
1263
1490
  },
1264
1491
  resolveHeaderParamsName(node, param) {
1265
- return ctx.resolveParamName(node, param);
1492
+ return this.resolveParamName(node, param);
1266
1493
  }
1267
1494
  };
1268
1495
  });
1269
1496
  //#endregion
1270
1497
  //#region src/plugin.ts
1271
1498
  /**
1272
- * Canonical plugin name for `@kubb/plugin-ts`, used to identify the plugin in driver lookups and warnings.
1499
+ * Canonical plugin name for `@kubb/plugin-ts`. Used for driver lookups and
1500
+ * cross-plugin dependency references.
1273
1501
  */
1274
1502
  const pluginTsName = "plugin-ts";
1275
1503
  /**
1276
- * The `@kubb/plugin-ts` plugin factory.
1277
- *
1278
- * Generates TypeScript type declarations from an OpenAPI/AST `RootNode`.
1279
- * Walks schemas and operations, delegates rendering to the active generators,
1280
- * and writes barrel files based on `output.barrelType`.
1504
+ * Generates TypeScript `type` aliases and `interface` declarations from an
1505
+ * OpenAPI spec. The foundation that every other Kubb plugin builds on:
1506
+ * clients, query hooks, mocks, and validators all reference the names this
1507
+ * plugin produces.
1281
1508
  *
1282
1509
  * @example
1283
1510
  * ```ts
1284
- * import pluginTs from '@kubb/plugin-ts'
1511
+ * import { defineConfig } from 'kubb'
1512
+ * import { pluginTs } from '@kubb/plugin-ts'
1285
1513
  *
1286
1514
  * export default defineConfig({
1287
- * plugins: [pluginTs({ output: { path: 'types' }, enumType: 'asConst' })],
1515
+ * input: { path: './petStore.yaml' },
1516
+ * output: { path: './src/gen' },
1517
+ * plugins: [
1518
+ * pluginTs({
1519
+ * output: { path: './types' },
1520
+ * enumType: 'asConst',
1521
+ * optionalType: 'questionTokenAndUndefined',
1522
+ * }),
1523
+ * ],
1288
1524
  * })
1289
1525
  * ```
1290
1526
  */
@@ -1299,7 +1535,7 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1299
1535
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1300
1536
  return `${camelCase(ctx.group)}Controller`;
1301
1537
  }
1302
- } : void 0;
1538
+ } : null;
1303
1539
  return {
1304
1540
  name: pluginTsName,
1305
1541
  options,