@kubb/plugin-ts 5.0.0-beta.64 → 5.0.0-beta.73

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
@@ -13,6 +13,15 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
13
  var __getOwnPropNames = Object.getOwnPropertyNames;
14
14
  var __getProtoOf = Object.getPrototypeOf;
15
15
  var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __exportAll = (all, no_symbols) => {
17
+ let target = {};
18
+ for (var name in all) __defProp(target, name, {
19
+ get: all[name],
20
+ enumerable: true
21
+ });
22
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
23
+ return target;
24
+ };
16
25
  var __copyProps = (to, from, except, desc) => {
17
26
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
18
27
  key = keys[i];
@@ -288,7 +297,59 @@ const PARAM_RANK = {
288
297
  };
289
298
  //#endregion
290
299
  //#region src/factory.ts
300
+ var factory_exports = /* @__PURE__ */ __exportAll({
301
+ SyntaxKind: () => SyntaxKind,
302
+ appendJSDocToNode: () => appendJSDocToNode,
303
+ buildIndexSignatures: () => buildIndexSignatures,
304
+ buildMemberNodes: () => buildMemberNodes,
305
+ buildPropertyType: () => buildPropertyType,
306
+ buildTupleNode: () => buildTupleNode,
307
+ constToTypeNode: () => constToTypeNode,
308
+ createArrayDeclaration: () => createArrayDeclaration,
309
+ createArrayTypeNode: () => createArrayTypeNode,
310
+ createEnumDeclaration: () => createEnumDeclaration,
311
+ createExportDeclaration: () => createExportDeclaration,
312
+ createFalse: () => createFalse,
313
+ createIdentifier: () => createIdentifier,
314
+ createImportDeclaration: () => createImportDeclaration,
315
+ createIndexSignature: () => createIndexSignature,
316
+ createInterfaceDeclaration: () => createInterfaceDeclaration,
317
+ createIntersectionDeclaration: () => createIntersectionDeclaration,
318
+ createJSDoc: () => createJSDoc,
319
+ createLiteralTypeNode: () => createLiteralTypeNode,
320
+ createNumericLiteral: () => createNumericLiteral,
321
+ createOmitDeclaration: () => createOmitDeclaration,
322
+ createOptionalTypeNode: () => createOptionalTypeNode,
323
+ createParameterSignature: () => createParameterSignature,
324
+ createPrefixUnaryExpression: () => createPrefixUnaryExpression,
325
+ createPropertySignature: () => createPropertySignature,
326
+ createQuestionToken: () => createQuestionToken,
327
+ createRestTypeNode: () => createRestTypeNode,
328
+ createStringLiteral: () => createStringLiteral,
329
+ createTrue: () => createTrue,
330
+ createTupleTypeNode: () => createTupleTypeNode,
331
+ createTypeAliasDeclaration: () => createTypeAliasDeclaration,
332
+ createTypeDeclaration: () => createTypeDeclaration,
333
+ createTypeLiteralNode: () => createTypeLiteralNode,
334
+ createTypeReferenceNode: () => createTypeReferenceNode,
335
+ createUnionDeclaration: () => createUnionDeclaration,
336
+ createUrlTemplateType: () => createUrlTemplateType,
337
+ dateOrStringNode: () => dateOrStringNode,
338
+ isNonNullable: () => isNonNullable$1,
339
+ keywordTypeNodes: () => keywordTypeNodes,
340
+ modifiers: () => modifiers,
341
+ syntaxKind: () => syntaxKind
342
+ });
291
343
  const { SyntaxKind, factory } = typescript.default;
344
+ /**
345
+ * Compares two strings by UTF-16 code unit, keeping sorted output identical across platforms
346
+ * regardless of locale.
347
+ */
348
+ function compareStrings(a, b) {
349
+ if (a < b) return -1;
350
+ if (a > b) return 1;
351
+ return 0;
352
+ }
292
353
  function isNumber(value) {
293
354
  return typeof value === "number" && !Number.isNaN(value);
294
355
  }
@@ -396,6 +457,17 @@ function createParameterSignature(name, { modifiers, dotDotDotToken, questionTok
396
457
  return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer);
397
458
  }
398
459
  /**
460
+ * Creates a JSDoc comment node from an array of comment strings.
461
+ * Returns null if no comments are provided.
462
+ */
463
+ function createJSDoc({ comments }) {
464
+ if (!comments.length) return null;
465
+ return factory.createJSDocComment(factory.createNodeArray(comments.map((comment, i) => {
466
+ if (i === comments.length - 1) return factory.createJSDocText(comment);
467
+ return factory.createJSDocText(`${comment}\n`);
468
+ })));
469
+ }
470
+ /**
399
471
  * Attaches JSDoc comments to an AST node as synthetic leading comments.
400
472
  * Filters out undefined comments before attaching.
401
473
  *
@@ -453,6 +525,50 @@ function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
453
525
  });
454
526
  }
455
527
  /**
528
+ * Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
529
+ * Optionally rename imported members with `propertyName` and `name` pairs.
530
+ *
531
+ * @example Default import
532
+ * `import Pet from './Pet'`
533
+ *
534
+ * @example Named imports with rename
535
+ * `import { Pet as Cat } from './Pet'`
536
+ *
537
+ * @example Namespace import
538
+ * `import * as Pet from './Pet'`
539
+ */
540
+ function createImportDeclaration({ name, path, isTypeOnly = false, isNameSpace = false }) {
541
+ if (!Array.isArray(name)) {
542
+ const importPropertyName = isNameSpace ? void 0 : factory.createIdentifier(name);
543
+ const importName = isNameSpace ? factory.createNamespaceImport(factory.createIdentifier(name)) : void 0;
544
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, importPropertyName, importName), factory.createStringLiteral(path), void 0);
545
+ }
546
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "object" ? a.propertyName : a, typeof b === "object" ? b.propertyName : b));
547
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamedImports(sortedName.map((item) => {
548
+ if (typeof item === "object") {
549
+ const obj = item;
550
+ if (obj.name) return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name));
551
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(obj.propertyName));
552
+ }
553
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(item));
554
+ }))), factory.createStringLiteral(path), void 0);
555
+ }
556
+ /**
557
+ * Creates an export declaration with support for named exports, namespace exports, and type-only exports.
558
+ * Sorts export names alphabetically for consistent output across platforms.
559
+ */
560
+ function createExportDeclaration({ path, asAlias, isTypeOnly = false, name }) {
561
+ if (name && !Array.isArray(name) && !asAlias) console.warn(`When using name as string, asAlias should be true ${name}`);
562
+ if (!Array.isArray(name)) {
563
+ const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name;
564
+ return factory.createExportDeclaration(void 0, isTypeOnly, asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : void 0, factory.createStringLiteral(path), void 0);
565
+ }
566
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "string" ? a : a.text, typeof b === "string" ? b : b.text));
567
+ return factory.createExportDeclaration(void 0, isTypeOnly, factory.createNamedExports(sortedName.map((propertyName) => {
568
+ return factory.createExportSpecifier(false, void 0, typeof propertyName === "string" ? factory.createIdentifier(propertyName) : propertyName);
569
+ })), factory.createStringLiteral(path), void 0);
570
+ }
571
+ /**
456
572
  * Apply casing transformation to enum keys
457
573
  */
458
574
  function applyEnumKeyCasing(key, casing = "none") {
@@ -787,6 +903,27 @@ function Type({ name, node, printer, enum: enumOptions, resolver }) {
787
903
  })] });
788
904
  }
789
905
  //#endregion
906
+ //#region ../../internals/shared/src/params.ts
907
+ const caseParamsCache = /* @__PURE__ */ new WeakMap();
908
+ /**
909
+ * Applies camelCase to parameter names and returns a new array without mutating the input.
910
+ *
911
+ * Run it before handing parameters to schema builders so output property keys get the right casing
912
+ * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
913
+ * original array is returned unchanged. Results are cached per input array.
914
+ */
915
+ function caseParams(params, casing) {
916
+ if (!casing) return params;
917
+ const cached = caseParamsCache.get(params);
918
+ if (cached) return cached;
919
+ const result = params.map((param) => ({
920
+ ...param,
921
+ name: camelCase(param.name)
922
+ }));
923
+ caseParamsCache.set(params, result);
924
+ return result;
925
+ }
926
+ //#endregion
790
927
  //#region ../../internals/shared/src/operation.ts
791
928
  /**
792
929
  * Maps a content type to the PascalCase suffix used to name per-content-type variants
@@ -837,7 +974,7 @@ function resolveContentTypeVariants(entries, baseName) {
837
974
  });
838
975
  }
839
976
  function getOperationParameters(node, options = {}) {
840
- const params = (0, _kubb_ast_utils.caseParams)(node.parameters, options.paramsCasing);
977
+ const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
841
978
  return {
842
979
  path: params.filter((param) => param.in === "path"),
843
980
  query: params.filter((param) => param.in === "query"),
@@ -893,6 +1030,7 @@ function buildPropertyJSDocComments(schema, optional) {
893
1030
  const isArray = meta?.primitive === "array";
894
1031
  const hasDescription = meta && "description" in meta && meta.description;
895
1032
  const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
1033
+ const exampleValues = meta?.examples ?? [];
896
1034
  return [
897
1035
  hasDescription ? `@description ${(0, _kubb_ast_utils.jsStringEscape)(meta.description)}` : null,
898
1036
  ...formatComment,
@@ -901,7 +1039,7 @@ function buildPropertyJSDocComments(schema, optional) {
901
1039
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
902
1040
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
903
1041
  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,
904
- meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
1042
+ ...exampleValues.map((example) => `@example ${example}`),
905
1043
  meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
906
1044
  ].filter(Boolean);
907
1045
  }
@@ -920,17 +1058,21 @@ function buildParams(node, { params, resolver }) {
920
1058
  });
921
1059
  }
922
1060
  function buildData(node, { resolver }) {
923
- const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
1061
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing: "original" });
1062
+ const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
1063
+ const hasRequiredPath = pathParams.some((param) => param.required);
1064
+ const hasRequiredQuery = queryParams.some((param) => param.required);
1065
+ const hasRequiredHeader = headerParams.some((param) => param.required);
924
1066
  return _kubb_core.ast.factory.createSchema({
925
1067
  type: "object",
926
1068
  deprecated: node.deprecated,
927
1069
  properties: [
928
1070
  _kubb_core.ast.factory.createProperty({
929
- name: "data",
930
- schema: node.requestBody?.content?.[0]?.schema ? _kubb_core.ast.factory.createSchema({
1071
+ name: "body",
1072
+ required: hasBody,
1073
+ schema: hasBody ? _kubb_core.ast.factory.createSchema({
931
1074
  type: "ref",
932
- name: resolver.resolveDataName(node),
933
- optional: true
1075
+ name: resolver.resolveDataName(node)
934
1076
  }) : _kubb_core.ast.factory.createSchema({
935
1077
  type: "never",
936
1078
  primitive: void 0,
@@ -938,11 +1080,14 @@ function buildData(node, { resolver }) {
938
1080
  })
939
1081
  }),
940
1082
  _kubb_core.ast.factory.createProperty({
941
- name: "pathParams",
942
- required: pathParams.length > 0,
943
- schema: pathParams.length > 0 ? buildParams(node, {
944
- params: pathParams,
945
- resolver
1083
+ name: "path",
1084
+ required: hasRequiredPath,
1085
+ schema: pathParams.length > 0 ? _kubb_core.ast.factory.createSchema({
1086
+ ...buildParams(node, {
1087
+ params: pathParams,
1088
+ resolver
1089
+ }),
1090
+ optional: !hasRequiredPath
946
1091
  }) : _kubb_core.ast.factory.createSchema({
947
1092
  type: "never",
948
1093
  primitive: void 0,
@@ -950,13 +1095,14 @@ function buildData(node, { resolver }) {
950
1095
  })
951
1096
  }),
952
1097
  _kubb_core.ast.factory.createProperty({
953
- name: "queryParams",
1098
+ name: "query",
1099
+ required: hasRequiredQuery,
954
1100
  schema: queryParams.length > 0 ? _kubb_core.ast.factory.createSchema({
955
1101
  ...buildParams(node, {
956
1102
  params: queryParams,
957
1103
  resolver
958
1104
  }),
959
- optional: true
1105
+ optional: !hasRequiredQuery
960
1106
  }) : _kubb_core.ast.factory.createSchema({
961
1107
  type: "never",
962
1108
  primitive: void 0,
@@ -964,26 +1110,19 @@ function buildData(node, { resolver }) {
964
1110
  })
965
1111
  }),
966
1112
  _kubb_core.ast.factory.createProperty({
967
- name: "headerParams",
1113
+ name: "headers",
1114
+ required: hasRequiredHeader,
968
1115
  schema: headerParams.length > 0 ? _kubb_core.ast.factory.createSchema({
969
1116
  ...buildParams(node, {
970
1117
  params: headerParams,
971
1118
  resolver
972
1119
  }),
973
- optional: true
1120
+ optional: !hasRequiredHeader
974
1121
  }) : _kubb_core.ast.factory.createSchema({
975
1122
  type: "never",
976
1123
  primitive: void 0,
977
1124
  optional: true
978
1125
  })
979
- }),
980
- _kubb_core.ast.factory.createProperty({
981
- name: "url",
982
- required: true,
983
- schema: _kubb_core.ast.factory.createSchema({
984
- type: "url",
985
- path: node.path
986
- })
987
1126
  })
988
1127
  ]
989
1128
  });
@@ -1015,9 +1154,7 @@ function buildResponseUnion(node, { resolver }) {
1015
1154
  }
1016
1155
  //#endregion
1017
1156
  //#region src/printers/printerTs.ts
1018
- function isNonNullable(value) {
1019
- return value !== null && value !== void 0;
1020
- }
1157
+ const { isNonNullable } = factory_exports;
1021
1158
  /**
1022
1159
  * TypeScript type printer built with `definePrinter`.
1023
1160
  *
@@ -1041,7 +1178,7 @@ function isNonNullable(value) {
1041
1178
  * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
1042
1179
  * ```
1043
1180
  */
1044
- const printerTs = _kubb_core.ast.definePrinter((options) => {
1181
+ const printerTs = _kubb_core.ast.createPrinter((options) => {
1045
1182
  const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType);
1046
1183
  return {
1047
1184
  name: "typescript",
@@ -1268,9 +1405,9 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1268
1405
  });
1269
1406
  },
1270
1407
  operation(node, ctx) {
1271
- const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1408
+ const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
1272
1409
  const { adapter, config, resolver, root } = ctx;
1273
- const params = (0, _kubb_ast_utils.caseParams)(node.parameters, paramsCasing);
1410
+ const params = caseParams(node.parameters, "camelcase");
1274
1411
  const meta = { file: resolver.resolveFile({
1275
1412
  name: node.operationId,
1276
1413
  extname: ".ts",
@@ -1550,7 +1687,7 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1550
1687
  const { output = {
1551
1688
  path: "types",
1552
1689
  barrel: { type: "named" }
1553
- }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, macros: userMacros, generators: userGenerators = [] } = options;
1690
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", printer, resolver: userResolver, macros: userMacros } = options;
1554
1691
  const groupConfig = createGroupConfig(group);
1555
1692
  const resolvedEnum = {
1556
1693
  type: enumOptions.type ?? "asConst",
@@ -1572,7 +1709,6 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1572
1709
  arrayType,
1573
1710
  enum: resolvedEnum,
1574
1711
  syntaxType,
1575
- paramsCasing,
1576
1712
  printer
1577
1713
  });
1578
1714
  ctx.setResolver(userResolver ? {
@@ -1581,14 +1717,123 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1581
1717
  } : resolverTs);
1582
1718
  if (userMacros?.length) ctx.setMacros(userMacros);
1583
1719
  ctx.addGenerator(typeGenerator);
1584
- for (const gen of userGenerators) ctx.addGenerator(gen);
1585
1720
  } }
1586
1721
  };
1587
1722
  });
1588
1723
  //#endregion
1724
+ //#region src/printers/functionParams.ts
1725
+ /**
1726
+ * Narrows a {@link TypeExpression} to a {@link TypeLiteralNode}.
1727
+ */
1728
+ function isTypeLiteral(type) {
1729
+ return typeof type !== "string" && type.kind === "TypeLiteral";
1730
+ }
1731
+ /**
1732
+ * Narrows a {@link TypeExpression} to an {@link IndexedAccessTypeNode}.
1733
+ */
1734
+ function isIndexedAccessType(type) {
1735
+ return typeof type !== "string" && type.kind === "IndexedAccessType";
1736
+ }
1737
+ /**
1738
+ * Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
1739
+ *
1740
+ * @example
1741
+ * ```ts
1742
+ * createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
1743
+ * // { petId: string }
1744
+ * ```
1745
+ */
1746
+ function createTypeLiteral(props) {
1747
+ return {
1748
+ kind: "TypeLiteral",
1749
+ ...props
1750
+ };
1751
+ }
1752
+ /**
1753
+ * Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
1754
+ *
1755
+ * @example
1756
+ * ```ts
1757
+ * createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
1758
+ * // DeletePetPathParams['petId']
1759
+ * ```
1760
+ */
1761
+ function createIndexedAccessType(props) {
1762
+ return {
1763
+ kind: "IndexedAccessType",
1764
+ ...props
1765
+ };
1766
+ }
1767
+ /**
1768
+ * Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
1769
+ *
1770
+ * @example
1771
+ * ```ts
1772
+ * createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
1773
+ * // { id, name }
1774
+ * ```
1775
+ */
1776
+ function createObjectBindingPattern(props) {
1777
+ return {
1778
+ kind: "ObjectBindingPattern",
1779
+ ...props
1780
+ };
1781
+ }
1782
+ /**
1783
+ * Creates a {@link FunctionParameterNode}. `optional` defaults to `false`.
1784
+ * Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
1785
+ * paired with a {@link TypeLiteralNode} type.
1786
+ *
1787
+ * @example Optional param
1788
+ * ```ts
1789
+ * createFunctionParameter({ name: 'params', type: 'QueryParams', optional: true })
1790
+ * // → params?: QueryParams
1791
+ * ```
1792
+ *
1793
+ * @example Destructured group
1794
+ * ```ts
1795
+ * createFunctionParameter({ properties: [{ name: 'id', type: 'string' }, { name: 'name', type: 'string', optional: true }], default: '{}' })
1796
+ * // → { id, name }: { id: string; name?: string } = {}
1797
+ * ```
1798
+ */
1799
+ function createFunctionParameter(input) {
1800
+ if ("properties" in input) return {
1801
+ kind: "FunctionParameter",
1802
+ name: createObjectBindingPattern({ elements: input.properties.map((p) => ({ name: p.name })) }),
1803
+ type: createTypeLiteral({ members: input.properties.map((p) => ({
1804
+ name: p.name,
1805
+ type: p.type,
1806
+ optional: p.optional ?? false
1807
+ })) }),
1808
+ optional: input.optional ?? false,
1809
+ ...input.default !== void 0 ? { default: input.default } : {}
1810
+ };
1811
+ return {
1812
+ kind: "FunctionParameter",
1813
+ optional: false,
1814
+ ...input
1815
+ };
1816
+ }
1817
+ /**
1818
+ * Creates a {@link FunctionParametersNode} from an ordered list of parameters.
1819
+ *
1820
+ * @example
1821
+ * ```ts
1822
+ * const empty = createFunctionParameters()
1823
+ * // { params: [] }
1824
+ * ```
1825
+ */
1826
+ function createFunctionParameters(props = {}) {
1827
+ return {
1828
+ kind: "FunctionParameters",
1829
+ params: [],
1830
+ ...props
1831
+ };
1832
+ }
1833
+ //#endregion
1589
1834
  //#region src/printers/functionPrinter.ts
1590
1835
  /**
1591
- * Renders a {@link ast.TypeExpression} to its TypeScript source.
1836
+ * Renders a {@link TypeExpression} to its TypeScript source.
1592
1837
  *
1593
1838
  * - a `string` is a type reference, returned as-is
1594
1839
  * - an `IndexedAccessType` becomes `objectType['indexType']`
@@ -1601,18 +1846,24 @@ function renderType(type, transformType) {
1601
1846
  const rendered = renderTypeExpression(type);
1602
1847
  return transformType ? transformType(rendered) : rendered;
1603
1848
  }
1849
+ /**
1850
+ * Renders an object-type key, quoting it as a string literal when it is not a valid
1851
+ * identifier so `{ 'content-type': string }` stays valid TypeScript.
1852
+ */
1853
+ function renderKey(name) {
1854
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
1855
+ }
1604
1856
  function renderTypeExpression(type) {
1605
1857
  if (typeof type === "string") return type;
1606
- if (_kubb_core.ast.indexedAccessTypeDef.is(type)) return `${type.target}['${type.key}']`;
1858
+ if (isIndexedAccessType(type)) return `${type.target}['${type.key}']`;
1607
1859
  return `{ ${type.members.map((member) => {
1608
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1609
1860
  const value = renderTypeExpression(member.type);
1610
- return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1861
+ return member.optional ? `${renderKey(member.name)}?: ${value}` : `${renderKey(member.name)}: ${value}`;
1611
1862
  }).join("; ")} }`;
1612
1863
  }
1613
1864
  function groupMembers(param) {
1614
1865
  if (typeof param.name === "string") return null;
1615
- return param.type && _kubb_core.ast.typeLiteralDef.is(param.type) ? param.type.members : [];
1866
+ return param.type && isTypeLiteral(param.type) ? param.type.members : [];
1616
1867
  }
1617
1868
  function rank(param) {
1618
1869
  if (param.rest) return PARAM_RANK.rest;
@@ -1624,35 +1875,68 @@ function rank(param) {
1624
1875
  function sortParams(params) {
1625
1876
  return params.toSorted((a, b) => rank(a) - rank(b));
1626
1877
  }
1627
- /**
1628
- * Orders a destructured group's binding elements and type members together,
1629
- * required fields first, matching how grouped children were sorted before.
1630
- */
1631
1878
  function sortedGroupMembers(name, type) {
1632
- const members = type && _kubb_core.ast.typeLiteralDef.is(type) ? type.members : [];
1879
+ const members = type && isTypeLiteral(type) ? type.members : [];
1633
1880
  const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
1634
1881
  return name.elements.map((element, index) => ({
1635
1882
  name: element.name,
1883
+ propertyName: element.propertyName,
1636
1884
  type: members[index]?.type,
1637
1885
  optional: members[index]?.optional
1638
1886
  })).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
1639
1887
  }
1640
1888
  /**
1889
+ * Renders one binding element: the local name, prefixed with its source key when the
1890
+ * binding renames the property, as in `{ petId: id }`.
1891
+ */
1892
+ function renderBindingMember(member, transformName) {
1893
+ const local = transformName ? transformName(member.name) : member.name;
1894
+ return member.propertyName ? `${member.propertyName}: ${local}` : local;
1895
+ }
1896
+ /**
1641
1897
  * Renders the type annotation of a destructured group. An inline object type is
1642
1898
  * rendered from the already-sorted members so its key order matches the binding;
1643
1899
  * a reference type is rendered as-is.
1644
1900
  */
1645
1901
  function renderGroupType(type, sorted, transformType) {
1646
1902
  if (!type) return void 0;
1647
- if (!_kubb_core.ast.typeLiteralDef.is(type)) return renderType(type);
1903
+ if (!isTypeLiteral(type)) return renderType(type);
1648
1904
  const typed = sorted.filter((member) => member.type !== void 0);
1649
1905
  if (!typed.length) return void 0;
1650
1906
  return `{ ${typed.map((member) => {
1651
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1907
+ const key = renderKey(member.propertyName ?? member.name);
1652
1908
  const value = renderType(member.type, transformType);
1653
1909
  return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1654
1910
  }).join("; ")} }`;
1655
1911
  }
1912
+ function printParameter(node, options) {
1913
+ const { mode, transformName, transformType } = options;
1914
+ const isGroup = typeof node.name !== "string";
1915
+ if (mode === "call") {
1916
+ if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => renderBindingMember(member)).join(", ")} }`;
1917
+ const name = transformName ? transformName(node.name) : node.name;
1918
+ return node.rest ? `...${name}` : name;
1919
+ }
1920
+ if (isGroup) {
1921
+ const sorted = sortedGroupMembers(node.name, node.type);
1922
+ const binding = `{ ${sorted.map((member) => renderBindingMember(member, transformName)).join(", ")} }`;
1923
+ const allOptional = sorted.every((member) => member.optional);
1924
+ const type = renderGroupType(node.type, sorted, transformType);
1925
+ if (type) {
1926
+ if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1927
+ return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1928
+ }
1929
+ return node.default ? `${binding} = ${node.default}` : binding;
1930
+ }
1931
+ const name = transformName ? transformName(node.name) : node.name;
1932
+ const type = node.type ? renderType(node.type, transformType) : void 0;
1933
+ if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1934
+ if (type) {
1935
+ if (node.optional) return `${name}?: ${type}`;
1936
+ return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
1937
+ }
1938
+ return node.default ? `${name} = ${node.default}` : name;
1939
+ }
1656
1940
  /**
1657
1941
  * Default function-signature printer. Renders a parameter list in one of two modes:
1658
1942
  * `declaration` for the function signature and `call` for the call arguments.
@@ -1671,46 +1955,206 @@ function renderGroupType(type, sorted, transformType) {
1671
1955
  * printer.print(sig) // → "petId: string, config: Config = {}"
1672
1956
  * ```
1673
1957
  */
1674
- const functionPrinter = _kubb_core.ast.createPrinterFactory((node) => node.kind)((options) => ({
1675
- name: "functionParameters",
1676
- options,
1677
- nodes: {
1678
- FunctionParameter(node) {
1679
- const { mode, transformName, transformType } = this.options;
1680
- const isGroup = typeof node.name !== "string";
1681
- if (mode === "call") {
1682
- if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => member.name).join(", ")} }`;
1683
- const name = transformName ? transformName(node.name) : node.name;
1684
- return node.rest ? `...${name}` : name;
1685
- }
1686
- if (isGroup) {
1687
- const sorted = sortedGroupMembers(node.name, node.type);
1688
- const binding = `{ ${sorted.map((member) => transformName ? transformName(member.name) : member.name).join(", ")} }`;
1689
- const allOptional = sorted.every((member) => member.optional);
1690
- const type = renderGroupType(node.type, sorted, transformType);
1691
- if (type) {
1692
- if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1693
- return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1694
- }
1695
- return node.default ? `${binding} = ${node.default}` : binding;
1696
- }
1697
- const name = transformName ? transformName(node.name) : node.name;
1698
- const type = node.type ? renderType(node.type, transformType) : void 0;
1699
- if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1700
- if (type) {
1701
- if (node.optional) return `${name}?: ${type}`;
1702
- return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
1703
- }
1704
- return node.default ? `${name} = ${node.default}` : name;
1705
- },
1706
- FunctionParameters(node) {
1707
- return sortParams(node.params).map((p) => this.transform(p)).filter(Boolean).join(", ");
1958
+ function functionPrinter(options) {
1959
+ return {
1960
+ name: "functionParameters",
1961
+ options,
1962
+ print(node) {
1963
+ return sortParams(node.params).map((p) => printParameter(p, options)).filter(Boolean).join(", ");
1708
1964
  }
1965
+ };
1966
+ }
1967
+ //#endregion
1968
+ //#region src/printers/operationParams.ts
1969
+ /**
1970
+ * Resolves the {@link TypeExpression} for an individual parameter.
1971
+ *
1972
+ * Without a resolver, it falls back to the schema primitive (a plain type-name string). When the
1973
+ * parameter belongs to a named group, it emits an `IndexedAccessType` like `GroupParams['petId']`,
1974
+ * otherwise the resolved individual name.
1975
+ */
1976
+ function resolveParamType({ node, param, resolver }) {
1977
+ if (!resolver) return param.schema.primitive ?? "unknown";
1978
+ const individualName = resolver.resolveParamName(node, param);
1979
+ const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
1980
+ const groupResolvers = {
1981
+ path: resolver.resolvePathParamsName,
1982
+ query: resolver.resolveQueryParamsName,
1983
+ header: resolver.resolveHeaderParamsName
1984
+ };
1985
+ const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
1986
+ if (groupName && groupName !== individualName) return createIndexedAccessType({
1987
+ target: groupName,
1988
+ key: param.name
1989
+ });
1990
+ return individualName;
1991
+ }
1992
+ /**
1993
+ * Derives a {@link ParamGroupType} for a query or header group from the resolver.
1994
+ *
1995
+ * Returns `null` when there is no resolver, no params, or the group name equals the
1996
+ * individual param name (so there is no real group to emit).
1997
+ */
1998
+ function resolveGroupType({ node, params, group, resolver }) {
1999
+ if (!resolver || !params.length) return null;
2000
+ const firstParam = params[0];
2001
+ const groupName = (group === "query" ? resolver.resolveQueryParamsName : resolver.resolveHeaderParamsName).call(resolver, node, firstParam);
2002
+ if (groupName === resolver.resolveParamName(node, firstParam)) return null;
2003
+ return {
2004
+ type: groupName,
2005
+ optional: params.every((p) => !p.required)
2006
+ };
2007
+ }
2008
+ /**
2009
+ * Converts an `OperationNode` into function parameters for code generation.
2010
+ *
2011
+ * Centralizes parameter grouping logic for all plugins. `paramsType` chooses between one
2012
+ * destructured object parameter (`object`) and separate top-level parameters (`inline`), while
2013
+ * `pathParamsType` controls how path params render in inline mode. Provide a `resolver` for type
2014
+ * name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
2015
+ */
2016
+ function createOperationParams(node, options) {
2017
+ const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
2018
+ const dataName = paramNames?.data ?? "data";
2019
+ const paramsName = paramNames?.params ?? "params";
2020
+ const headersName = paramNames?.headers ?? "headers";
2021
+ const pathName = paramNames?.path ?? "pathParams";
2022
+ const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
2023
+ const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
2024
+ const casedParams = caseParams(node.parameters, paramsCasing);
2025
+ const pathParams = casedParams.filter((p) => p.in === "path");
2026
+ const queryParams = casedParams.filter((p) => p.in === "query");
2027
+ const headerParams = casedParams.filter((p) => p.in === "header");
2028
+ const toProperty = (param) => ({
2029
+ name: param.name,
2030
+ type: wrapTypeExpression(resolveParamType({
2031
+ node,
2032
+ param,
2033
+ resolver
2034
+ })),
2035
+ optional: !param.required
2036
+ });
2037
+ const emptyObjectDefault = (props) => props.every((p) => p.optional) ? "{}" : void 0;
2038
+ const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.resolveDataName(node) ?? "unknown") : void 0;
2039
+ const bodyProperty = bodyType ? [{
2040
+ name: dataName,
2041
+ type: bodyType,
2042
+ optional: !(node.requestBody?.required ?? false)
2043
+ }] : [];
2044
+ const trailingGroups = [{
2045
+ name: paramsName,
2046
+ node,
2047
+ params: queryParams,
2048
+ groupType: resolveGroupType({
2049
+ node,
2050
+ params: queryParams,
2051
+ group: "query",
2052
+ resolver
2053
+ }),
2054
+ resolver,
2055
+ wrapType
2056
+ }, {
2057
+ name: headersName,
2058
+ node,
2059
+ params: headerParams,
2060
+ groupType: resolveGroupType({
2061
+ node,
2062
+ params: headerParams,
2063
+ group: "header",
2064
+ resolver
2065
+ }),
2066
+ resolver,
2067
+ wrapType
2068
+ }];
2069
+ const params = [];
2070
+ if (paramsType === "object") {
2071
+ const children = [
2072
+ ...pathParams.map(toProperty),
2073
+ ...bodyProperty,
2074
+ ...trailingGroups.flatMap(buildGroupProperty)
2075
+ ];
2076
+ if (children.length) params.push(createFunctionParameter({
2077
+ properties: children,
2078
+ default: emptyObjectDefault(children)
2079
+ }));
2080
+ } else {
2081
+ if (pathParamsType === "inlineSpread" && pathParams.length) {
2082
+ const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]);
2083
+ params.push(createFunctionParameter({
2084
+ name: pathName,
2085
+ type: spreadType ? wrapType(spreadType) : void 0,
2086
+ rest: true
2087
+ }));
2088
+ } else if (pathParamsType === "inline") params.push(...pathParams.map((p) => createFunctionParameter(toProperty(p))));
2089
+ else if (pathParams.length) {
2090
+ const pathChildren = pathParams.map(toProperty);
2091
+ params.push(createFunctionParameter({
2092
+ properties: pathChildren,
2093
+ default: pathParamsDefault ?? emptyObjectDefault(pathChildren)
2094
+ }));
2095
+ }
2096
+ params.push(...bodyProperty.map((p) => createFunctionParameter(p)));
2097
+ params.push(...trailingGroups.flatMap(buildGroupParam));
1709
2098
  }
1710
- }));
2099
+ params.push(...extraParams);
2100
+ return createFunctionParameters({ params });
2101
+ }
2102
+ /**
2103
+ * Builds the property descriptor for a query or header group.
2104
+ * Returns an empty array when there are no params to emit.
2105
+ *
2106
+ * A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
2107
+ * {@link TypeLiteralNode} from the individual params.
2108
+ */
2109
+ function buildGroupProperty({ name, node, params, groupType, resolver, wrapType }) {
2110
+ if (groupType) return [{
2111
+ name,
2112
+ type: typeof groupType.type === "string" ? wrapType(groupType.type) : groupType.type,
2113
+ optional: groupType.optional
2114
+ }];
2115
+ if (params.length) return [{
2116
+ name,
2117
+ type: buildTypeLiteral({
2118
+ node,
2119
+ params,
2120
+ resolver
2121
+ }),
2122
+ optional: params.every((p) => !p.required)
2123
+ }];
2124
+ return [];
2125
+ }
2126
+ /**
2127
+ * Builds a single {@link FunctionParameterNode} for a query or header group.
2128
+ * Returns an empty array when there are no params to emit.
2129
+ */
2130
+ function buildGroupParam(args) {
2131
+ return buildGroupProperty(args).map((p) => createFunctionParameter(p));
2132
+ }
2133
+ /**
2134
+ * Builds a {@link TypeLiteralNode} for an inline anonymous type grouping named fields.
2135
+ *
2136
+ * Used when query or header parameters have no dedicated group type name.
2137
+ */
2138
+ function buildTypeLiteral({ node, params, resolver }) {
2139
+ return createTypeLiteral({ members: params.map((p) => ({
2140
+ name: p.name,
2141
+ type: resolveParamType({
2142
+ node,
2143
+ param: p,
2144
+ resolver
2145
+ }),
2146
+ optional: !p.required
2147
+ })) });
2148
+ }
1711
2149
  //#endregion
1712
2150
  exports.Enum = Enum;
1713
2151
  exports.Type = Type;
2152
+ exports.createFunctionParameter = createFunctionParameter;
2153
+ exports.createFunctionParameters = createFunctionParameters;
2154
+ exports.createIndexedAccessType = createIndexedAccessType;
2155
+ exports.createObjectBindingPattern = createObjectBindingPattern;
2156
+ exports.createOperationParams = createOperationParams;
2157
+ exports.createTypeLiteral = createTypeLiteral;
1714
2158
  exports.default = pluginTs;
1715
2159
  exports.functionPrinter = functionPrinter;
1716
2160
  exports.pluginTs = pluginTs;