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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as __name } from "./chunk-C0LytTxp.js";
2
- import { caseParams, extractRefName, isStringType, jsStringEscape, mapSchemaItems, mapSchemaProperties, stringify, syncSchemaRef, trimQuotes } from "@kubb/ast/utils";
1
+ import { n as __name, t as __exportAll } from "./rolldown-runtime-CNktS9qV.js";
2
+ import { extractRefName, isStringType, jsStringEscape, mapSchemaItems, mapSchemaProperties, stringify, syncSchemaRef, trimQuotes } from "@kubb/ast/utils";
3
3
  import { parserTs } from "@kubb/parser-ts";
4
4
  import { File, jsxRenderer } from "@kubb/renderer-jsx";
5
5
  import ts from "typescript";
@@ -258,7 +258,59 @@ const PARAM_RANK = {
258
258
  };
259
259
  //#endregion
260
260
  //#region src/factory.ts
261
+ var factory_exports = /* @__PURE__ */ __exportAll({
262
+ SyntaxKind: () => SyntaxKind,
263
+ appendJSDocToNode: () => appendJSDocToNode,
264
+ buildIndexSignatures: () => buildIndexSignatures,
265
+ buildMemberNodes: () => buildMemberNodes,
266
+ buildPropertyType: () => buildPropertyType,
267
+ buildTupleNode: () => buildTupleNode,
268
+ constToTypeNode: () => constToTypeNode,
269
+ createArrayDeclaration: () => createArrayDeclaration,
270
+ createArrayTypeNode: () => createArrayTypeNode,
271
+ createEnumDeclaration: () => createEnumDeclaration,
272
+ createExportDeclaration: () => createExportDeclaration,
273
+ createFalse: () => createFalse,
274
+ createIdentifier: () => createIdentifier,
275
+ createImportDeclaration: () => createImportDeclaration,
276
+ createIndexSignature: () => createIndexSignature,
277
+ createInterfaceDeclaration: () => createInterfaceDeclaration,
278
+ createIntersectionDeclaration: () => createIntersectionDeclaration,
279
+ createJSDoc: () => createJSDoc,
280
+ createLiteralTypeNode: () => createLiteralTypeNode,
281
+ createNumericLiteral: () => createNumericLiteral,
282
+ createOmitDeclaration: () => createOmitDeclaration,
283
+ createOptionalTypeNode: () => createOptionalTypeNode,
284
+ createParameterSignature: () => createParameterSignature,
285
+ createPrefixUnaryExpression: () => createPrefixUnaryExpression,
286
+ createPropertySignature: () => createPropertySignature,
287
+ createQuestionToken: () => createQuestionToken,
288
+ createRestTypeNode: () => createRestTypeNode,
289
+ createStringLiteral: () => createStringLiteral,
290
+ createTrue: () => createTrue,
291
+ createTupleTypeNode: () => createTupleTypeNode,
292
+ createTypeAliasDeclaration: () => createTypeAliasDeclaration,
293
+ createTypeDeclaration: () => createTypeDeclaration,
294
+ createTypeLiteralNode: () => createTypeLiteralNode,
295
+ createTypeReferenceNode: () => createTypeReferenceNode,
296
+ createUnionDeclaration: () => createUnionDeclaration,
297
+ createUrlTemplateType: () => createUrlTemplateType,
298
+ dateOrStringNode: () => dateOrStringNode,
299
+ isNonNullable: () => isNonNullable$1,
300
+ keywordTypeNodes: () => keywordTypeNodes,
301
+ modifiers: () => modifiers,
302
+ syntaxKind: () => syntaxKind
303
+ });
261
304
  const { SyntaxKind, factory } = ts;
305
+ /**
306
+ * Compares two strings by UTF-16 code unit, keeping sorted output identical across platforms
307
+ * regardless of locale.
308
+ */
309
+ function compareStrings(a, b) {
310
+ if (a < b) return -1;
311
+ if (a > b) return 1;
312
+ return 0;
313
+ }
262
314
  function isNumber(value) {
263
315
  return typeof value === "number" && !Number.isNaN(value);
264
316
  }
@@ -366,6 +418,17 @@ function createParameterSignature(name, { modifiers, dotDotDotToken, questionTok
366
418
  return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer);
367
419
  }
368
420
  /**
421
+ * Creates a JSDoc comment node from an array of comment strings.
422
+ * Returns null if no comments are provided.
423
+ */
424
+ function createJSDoc({ comments }) {
425
+ if (!comments.length) return null;
426
+ return factory.createJSDocComment(factory.createNodeArray(comments.map((comment, i) => {
427
+ if (i === comments.length - 1) return factory.createJSDocText(comment);
428
+ return factory.createJSDocText(`${comment}\n`);
429
+ })));
430
+ }
431
+ /**
369
432
  * Attaches JSDoc comments to an AST node as synthetic leading comments.
370
433
  * Filters out undefined comments before attaching.
371
434
  *
@@ -423,6 +486,50 @@ function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
423
486
  });
424
487
  }
425
488
  /**
489
+ * Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
490
+ * Optionally rename imported members with `propertyName` and `name` pairs.
491
+ *
492
+ * @example Default import
493
+ * `import Pet from './Pet'`
494
+ *
495
+ * @example Named imports with rename
496
+ * `import { Pet as Cat } from './Pet'`
497
+ *
498
+ * @example Namespace import
499
+ * `import * as Pet from './Pet'`
500
+ */
501
+ function createImportDeclaration({ name, path, isTypeOnly = false, isNameSpace = false }) {
502
+ if (!Array.isArray(name)) {
503
+ const importPropertyName = isNameSpace ? void 0 : factory.createIdentifier(name);
504
+ const importName = isNameSpace ? factory.createNamespaceImport(factory.createIdentifier(name)) : void 0;
505
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, importPropertyName, importName), factory.createStringLiteral(path), void 0);
506
+ }
507
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "object" ? a.propertyName : a, typeof b === "object" ? b.propertyName : b));
508
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamedImports(sortedName.map((item) => {
509
+ if (typeof item === "object") {
510
+ const obj = item;
511
+ if (obj.name) return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name));
512
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(obj.propertyName));
513
+ }
514
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(item));
515
+ }))), factory.createStringLiteral(path), void 0);
516
+ }
517
+ /**
518
+ * Creates an export declaration with support for named exports, namespace exports, and type-only exports.
519
+ * Sorts export names alphabetically for consistent output across platforms.
520
+ */
521
+ function createExportDeclaration({ path, asAlias, isTypeOnly = false, name }) {
522
+ if (name && !Array.isArray(name) && !asAlias) console.warn(`When using name as string, asAlias should be true ${name}`);
523
+ if (!Array.isArray(name)) {
524
+ const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name;
525
+ return factory.createExportDeclaration(void 0, isTypeOnly, asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : void 0, factory.createStringLiteral(path), void 0);
526
+ }
527
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "string" ? a : a.text, typeof b === "string" ? b : b.text));
528
+ return factory.createExportDeclaration(void 0, isTypeOnly, factory.createNamedExports(sortedName.map((propertyName) => {
529
+ return factory.createExportSpecifier(false, void 0, typeof propertyName === "string" ? factory.createIdentifier(propertyName) : propertyName);
530
+ })), factory.createStringLiteral(path), void 0);
531
+ }
532
+ /**
426
533
  * Apply casing transformation to enum keys
427
534
  */
428
535
  function applyEnumKeyCasing(key, casing = "none") {
@@ -757,6 +864,27 @@ function Type({ name, node, printer, enum: enumOptions, resolver }) {
757
864
  })] });
758
865
  }
759
866
  //#endregion
867
+ //#region ../../internals/shared/src/params.ts
868
+ const caseParamsCache = /* @__PURE__ */ new WeakMap();
869
+ /**
870
+ * Applies camelCase to parameter names and returns a new array without mutating the input.
871
+ *
872
+ * Run it before handing parameters to schema builders so output property keys get the right casing
873
+ * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
874
+ * original array is returned unchanged. Results are cached per input array.
875
+ */
876
+ function caseParams(params, casing) {
877
+ if (!casing) return params;
878
+ const cached = caseParamsCache.get(params);
879
+ if (cached) return cached;
880
+ const result = params.map((param) => ({
881
+ ...param,
882
+ name: camelCase(param.name)
883
+ }));
884
+ caseParamsCache.set(params, result);
885
+ return result;
886
+ }
887
+ //#endregion
760
888
  //#region ../../internals/shared/src/operation.ts
761
889
  /**
762
890
  * Maps a content type to the PascalCase suffix used to name per-content-type variants
@@ -807,7 +935,7 @@ function resolveContentTypeVariants(entries, baseName) {
807
935
  });
808
936
  }
809
937
  function getOperationParameters(node, options = {}) {
810
- const params = caseParams(node.parameters, options.paramsCasing);
938
+ const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
811
939
  return {
812
940
  path: params.filter((param) => param.in === "path"),
813
941
  query: params.filter((param) => param.in === "query"),
@@ -863,6 +991,7 @@ function buildPropertyJSDocComments(schema, optional) {
863
991
  const isArray = meta?.primitive === "array";
864
992
  const hasDescription = meta && "description" in meta && meta.description;
865
993
  const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
994
+ const exampleValues = meta?.examples ?? [];
866
995
  return [
867
996
  hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
868
997
  ...formatComment,
@@ -871,7 +1000,7 @@ function buildPropertyJSDocComments(schema, optional) {
871
1000
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
872
1001
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
873
1002
  meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
874
- meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
1003
+ ...exampleValues.map((example) => `@example ${example}`),
875
1004
  meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
876
1005
  ].filter(Boolean);
877
1006
  }
@@ -890,17 +1019,21 @@ function buildParams(node, { params, resolver }) {
890
1019
  });
891
1020
  }
892
1021
  function buildData(node, { resolver }) {
893
- const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
1022
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing: "original" });
1023
+ const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
1024
+ const hasRequiredPath = pathParams.some((param) => param.required);
1025
+ const hasRequiredQuery = queryParams.some((param) => param.required);
1026
+ const hasRequiredHeader = headerParams.some((param) => param.required);
894
1027
  return ast.factory.createSchema({
895
1028
  type: "object",
896
1029
  deprecated: node.deprecated,
897
1030
  properties: [
898
1031
  ast.factory.createProperty({
899
- name: "data",
900
- schema: node.requestBody?.content?.[0]?.schema ? ast.factory.createSchema({
1032
+ name: "body",
1033
+ required: hasBody,
1034
+ schema: hasBody ? ast.factory.createSchema({
901
1035
  type: "ref",
902
- name: resolver.resolveDataName(node),
903
- optional: true
1036
+ name: resolver.resolveDataName(node)
904
1037
  }) : ast.factory.createSchema({
905
1038
  type: "never",
906
1039
  primitive: void 0,
@@ -908,11 +1041,14 @@ function buildData(node, { resolver }) {
908
1041
  })
909
1042
  }),
910
1043
  ast.factory.createProperty({
911
- name: "pathParams",
912
- required: pathParams.length > 0,
913
- schema: pathParams.length > 0 ? buildParams(node, {
914
- params: pathParams,
915
- resolver
1044
+ name: "path",
1045
+ required: hasRequiredPath,
1046
+ schema: pathParams.length > 0 ? ast.factory.createSchema({
1047
+ ...buildParams(node, {
1048
+ params: pathParams,
1049
+ resolver
1050
+ }),
1051
+ optional: !hasRequiredPath
916
1052
  }) : ast.factory.createSchema({
917
1053
  type: "never",
918
1054
  primitive: void 0,
@@ -920,13 +1056,14 @@ function buildData(node, { resolver }) {
920
1056
  })
921
1057
  }),
922
1058
  ast.factory.createProperty({
923
- name: "queryParams",
1059
+ name: "query",
1060
+ required: hasRequiredQuery,
924
1061
  schema: queryParams.length > 0 ? ast.factory.createSchema({
925
1062
  ...buildParams(node, {
926
1063
  params: queryParams,
927
1064
  resolver
928
1065
  }),
929
- optional: true
1066
+ optional: !hasRequiredQuery
930
1067
  }) : ast.factory.createSchema({
931
1068
  type: "never",
932
1069
  primitive: void 0,
@@ -934,26 +1071,19 @@ function buildData(node, { resolver }) {
934
1071
  })
935
1072
  }),
936
1073
  ast.factory.createProperty({
937
- name: "headerParams",
1074
+ name: "headers",
1075
+ required: hasRequiredHeader,
938
1076
  schema: headerParams.length > 0 ? ast.factory.createSchema({
939
1077
  ...buildParams(node, {
940
1078
  params: headerParams,
941
1079
  resolver
942
1080
  }),
943
- optional: true
1081
+ optional: !hasRequiredHeader
944
1082
  }) : ast.factory.createSchema({
945
1083
  type: "never",
946
1084
  primitive: void 0,
947
1085
  optional: true
948
1086
  })
949
- }),
950
- ast.factory.createProperty({
951
- name: "url",
952
- required: true,
953
- schema: ast.factory.createSchema({
954
- type: "url",
955
- path: node.path
956
- })
957
1087
  })
958
1088
  ]
959
1089
  });
@@ -985,9 +1115,7 @@ function buildResponseUnion(node, { resolver }) {
985
1115
  }
986
1116
  //#endregion
987
1117
  //#region src/printers/printerTs.ts
988
- function isNonNullable(value) {
989
- return value !== null && value !== void 0;
990
- }
1118
+ const { isNonNullable } = factory_exports;
991
1119
  /**
992
1120
  * TypeScript type printer built with `definePrinter`.
993
1121
  *
@@ -1011,7 +1139,7 @@ function isNonNullable(value) {
1011
1139
  * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
1012
1140
  * ```
1013
1141
  */
1014
- const printerTs = ast.definePrinter((options) => {
1142
+ const printerTs = ast.createPrinter((options) => {
1015
1143
  const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType);
1016
1144
  return {
1017
1145
  name: "typescript",
@@ -1238,9 +1366,9 @@ const typeGenerator = defineGenerator({
1238
1366
  });
1239
1367
  },
1240
1368
  operation(node, ctx) {
1241
- const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1369
+ const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
1242
1370
  const { adapter, config, resolver, root } = ctx;
1243
- const params = caseParams(node.parameters, paramsCasing);
1371
+ const params = caseParams(node.parameters, "camelcase");
1244
1372
  const meta = { file: resolver.resolveFile({
1245
1373
  name: node.operationId,
1246
1374
  extname: ".ts",
@@ -1520,7 +1648,7 @@ const pluginTs = definePlugin((options) => {
1520
1648
  const { output = {
1521
1649
  path: "types",
1522
1650
  barrel: { type: "named" }
1523
- }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, macros: userMacros, generators: userGenerators = [] } = options;
1651
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", printer, resolver: userResolver, macros: userMacros } = options;
1524
1652
  const groupConfig = createGroupConfig(group);
1525
1653
  const resolvedEnum = {
1526
1654
  type: enumOptions.type ?? "asConst",
@@ -1542,7 +1670,6 @@ const pluginTs = definePlugin((options) => {
1542
1670
  arrayType,
1543
1671
  enum: resolvedEnum,
1544
1672
  syntaxType,
1545
- paramsCasing,
1546
1673
  printer
1547
1674
  });
1548
1675
  ctx.setResolver(userResolver ? {
@@ -1551,14 +1678,123 @@ const pluginTs = definePlugin((options) => {
1551
1678
  } : resolverTs);
1552
1679
  if (userMacros?.length) ctx.setMacros(userMacros);
1553
1680
  ctx.addGenerator(typeGenerator);
1554
- for (const gen of userGenerators) ctx.addGenerator(gen);
1555
1681
  } }
1556
1682
  };
1557
1683
  });
1558
1684
  //#endregion
1685
+ //#region src/printers/functionParams.ts
1686
+ /**
1687
+ * Narrows a {@link TypeExpression} to a {@link TypeLiteralNode}.
1688
+ */
1689
+ function isTypeLiteral(type) {
1690
+ return typeof type !== "string" && type.kind === "TypeLiteral";
1691
+ }
1692
+ /**
1693
+ * Narrows a {@link TypeExpression} to an {@link IndexedAccessTypeNode}.
1694
+ */
1695
+ function isIndexedAccessType(type) {
1696
+ return typeof type !== "string" && type.kind === "IndexedAccessType";
1697
+ }
1698
+ /**
1699
+ * Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
1700
+ *
1701
+ * @example
1702
+ * ```ts
1703
+ * createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
1704
+ * // { petId: string }
1705
+ * ```
1706
+ */
1707
+ function createTypeLiteral(props) {
1708
+ return {
1709
+ kind: "TypeLiteral",
1710
+ ...props
1711
+ };
1712
+ }
1713
+ /**
1714
+ * Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
1715
+ *
1716
+ * @example
1717
+ * ```ts
1718
+ * createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
1719
+ * // DeletePetPathParams['petId']
1720
+ * ```
1721
+ */
1722
+ function createIndexedAccessType(props) {
1723
+ return {
1724
+ kind: "IndexedAccessType",
1725
+ ...props
1726
+ };
1727
+ }
1728
+ /**
1729
+ * Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
1730
+ *
1731
+ * @example
1732
+ * ```ts
1733
+ * createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
1734
+ * // { id, name }
1735
+ * ```
1736
+ */
1737
+ function createObjectBindingPattern(props) {
1738
+ return {
1739
+ kind: "ObjectBindingPattern",
1740
+ ...props
1741
+ };
1742
+ }
1743
+ /**
1744
+ * Creates a {@link FunctionParameterNode}. `optional` defaults to `false`.
1745
+ * Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
1746
+ * paired with a {@link TypeLiteralNode} type.
1747
+ *
1748
+ * @example Optional param
1749
+ * ```ts
1750
+ * createFunctionParameter({ name: 'params', type: 'QueryParams', optional: true })
1751
+ * // → params?: QueryParams
1752
+ * ```
1753
+ *
1754
+ * @example Destructured group
1755
+ * ```ts
1756
+ * createFunctionParameter({ properties: [{ name: 'id', type: 'string' }, { name: 'name', type: 'string', optional: true }], default: '{}' })
1757
+ * // → { id, name }: { id: string; name?: string } = {}
1758
+ * ```
1759
+ */
1760
+ function createFunctionParameter(input) {
1761
+ if ("properties" in input) return {
1762
+ kind: "FunctionParameter",
1763
+ name: createObjectBindingPattern({ elements: input.properties.map((p) => ({ name: p.name })) }),
1764
+ type: createTypeLiteral({ members: input.properties.map((p) => ({
1765
+ name: p.name,
1766
+ type: p.type,
1767
+ optional: p.optional ?? false
1768
+ })) }),
1769
+ optional: input.optional ?? false,
1770
+ ...input.default !== void 0 ? { default: input.default } : {}
1771
+ };
1772
+ return {
1773
+ kind: "FunctionParameter",
1774
+ optional: false,
1775
+ ...input
1776
+ };
1777
+ }
1778
+ /**
1779
+ * Creates a {@link FunctionParametersNode} from an ordered list of parameters.
1780
+ *
1781
+ * @example
1782
+ * ```ts
1783
+ * const empty = createFunctionParameters()
1784
+ * // { params: [] }
1785
+ * ```
1786
+ */
1787
+ function createFunctionParameters(props = {}) {
1788
+ return {
1789
+ kind: "FunctionParameters",
1790
+ params: [],
1791
+ ...props
1792
+ };
1793
+ }
1794
+ //#endregion
1559
1795
  //#region src/printers/functionPrinter.ts
1560
1796
  /**
1561
- * Renders a {@link ast.TypeExpression} to its TypeScript source.
1797
+ * Renders a {@link TypeExpression} to its TypeScript source.
1562
1798
  *
1563
1799
  * - a `string` is a type reference, returned as-is
1564
1800
  * - an `IndexedAccessType` becomes `objectType['indexType']`
@@ -1571,18 +1807,24 @@ function renderType(type, transformType) {
1571
1807
  const rendered = renderTypeExpression(type);
1572
1808
  return transformType ? transformType(rendered) : rendered;
1573
1809
  }
1810
+ /**
1811
+ * Renders an object-type key, quoting it as a string literal when it is not a valid
1812
+ * identifier so `{ 'content-type': string }` stays valid TypeScript.
1813
+ */
1814
+ function renderKey(name) {
1815
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
1816
+ }
1574
1817
  function renderTypeExpression(type) {
1575
1818
  if (typeof type === "string") return type;
1576
- if (ast.indexedAccessTypeDef.is(type)) return `${type.target}['${type.key}']`;
1819
+ if (isIndexedAccessType(type)) return `${type.target}['${type.key}']`;
1577
1820
  return `{ ${type.members.map((member) => {
1578
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1579
1821
  const value = renderTypeExpression(member.type);
1580
- return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1822
+ return member.optional ? `${renderKey(member.name)}?: ${value}` : `${renderKey(member.name)}: ${value}`;
1581
1823
  }).join("; ")} }`;
1582
1824
  }
1583
1825
  function groupMembers(param) {
1584
1826
  if (typeof param.name === "string") return null;
1585
- return param.type && ast.typeLiteralDef.is(param.type) ? param.type.members : [];
1827
+ return param.type && isTypeLiteral(param.type) ? param.type.members : [];
1586
1828
  }
1587
1829
  function rank(param) {
1588
1830
  if (param.rest) return PARAM_RANK.rest;
@@ -1594,35 +1836,68 @@ function rank(param) {
1594
1836
  function sortParams(params) {
1595
1837
  return params.toSorted((a, b) => rank(a) - rank(b));
1596
1838
  }
1597
- /**
1598
- * Orders a destructured group's binding elements and type members together,
1599
- * required fields first, matching how grouped children were sorted before.
1600
- */
1601
1839
  function sortedGroupMembers(name, type) {
1602
- const members = type && ast.typeLiteralDef.is(type) ? type.members : [];
1840
+ const members = type && isTypeLiteral(type) ? type.members : [];
1603
1841
  const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
1604
1842
  return name.elements.map((element, index) => ({
1605
1843
  name: element.name,
1844
+ propertyName: element.propertyName,
1606
1845
  type: members[index]?.type,
1607
1846
  optional: members[index]?.optional
1608
1847
  })).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
1609
1848
  }
1610
1849
  /**
1850
+ * Renders one binding element: the local name, prefixed with its source key when the
1851
+ * binding renames the property, as in `{ petId: id }`.
1852
+ */
1853
+ function renderBindingMember(member, transformName) {
1854
+ const local = transformName ? transformName(member.name) : member.name;
1855
+ return member.propertyName ? `${member.propertyName}: ${local}` : local;
1856
+ }
1857
+ /**
1611
1858
  * Renders the type annotation of a destructured group. An inline object type is
1612
1859
  * rendered from the already-sorted members so its key order matches the binding;
1613
1860
  * a reference type is rendered as-is.
1614
1861
  */
1615
1862
  function renderGroupType(type, sorted, transformType) {
1616
1863
  if (!type) return void 0;
1617
- if (!ast.typeLiteralDef.is(type)) return renderType(type);
1864
+ if (!isTypeLiteral(type)) return renderType(type);
1618
1865
  const typed = sorted.filter((member) => member.type !== void 0);
1619
1866
  if (!typed.length) return void 0;
1620
1867
  return `{ ${typed.map((member) => {
1621
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1868
+ const key = renderKey(member.propertyName ?? member.name);
1622
1869
  const value = renderType(member.type, transformType);
1623
1870
  return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1624
1871
  }).join("; ")} }`;
1625
1872
  }
1873
+ function printParameter(node, options) {
1874
+ const { mode, transformName, transformType } = options;
1875
+ const isGroup = typeof node.name !== "string";
1876
+ if (mode === "call") {
1877
+ if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => renderBindingMember(member)).join(", ")} }`;
1878
+ const name = transformName ? transformName(node.name) : node.name;
1879
+ return node.rest ? `...${name}` : name;
1880
+ }
1881
+ if (isGroup) {
1882
+ const sorted = sortedGroupMembers(node.name, node.type);
1883
+ const binding = `{ ${sorted.map((member) => renderBindingMember(member, transformName)).join(", ")} }`;
1884
+ const allOptional = sorted.every((member) => member.optional);
1885
+ const type = renderGroupType(node.type, sorted, transformType);
1886
+ if (type) {
1887
+ if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1888
+ return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1889
+ }
1890
+ return node.default ? `${binding} = ${node.default}` : binding;
1891
+ }
1892
+ const name = transformName ? transformName(node.name) : node.name;
1893
+ const type = node.type ? renderType(node.type, transformType) : void 0;
1894
+ if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1895
+ if (type) {
1896
+ if (node.optional) return `${name}?: ${type}`;
1897
+ return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
1898
+ }
1899
+ return node.default ? `${name} = ${node.default}` : name;
1900
+ }
1626
1901
  /**
1627
1902
  * Default function-signature printer. Renders a parameter list in one of two modes:
1628
1903
  * `declaration` for the function signature and `call` for the call arguments.
@@ -1641,44 +1916,198 @@ function renderGroupType(type, sorted, transformType) {
1641
1916
  * printer.print(sig) // → "petId: string, config: Config = {}"
1642
1917
  * ```
1643
1918
  */
1644
- const functionPrinter = ast.createPrinterFactory((node) => node.kind)((options) => ({
1645
- name: "functionParameters",
1646
- options,
1647
- nodes: {
1648
- FunctionParameter(node) {
1649
- const { mode, transformName, transformType } = this.options;
1650
- const isGroup = typeof node.name !== "string";
1651
- if (mode === "call") {
1652
- if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => member.name).join(", ")} }`;
1653
- const name = transformName ? transformName(node.name) : node.name;
1654
- return node.rest ? `...${name}` : name;
1655
- }
1656
- if (isGroup) {
1657
- const sorted = sortedGroupMembers(node.name, node.type);
1658
- const binding = `{ ${sorted.map((member) => transformName ? transformName(member.name) : member.name).join(", ")} }`;
1659
- const allOptional = sorted.every((member) => member.optional);
1660
- const type = renderGroupType(node.type, sorted, transformType);
1661
- if (type) {
1662
- if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1663
- return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1664
- }
1665
- return node.default ? `${binding} = ${node.default}` : binding;
1666
- }
1667
- const name = transformName ? transformName(node.name) : node.name;
1668
- const type = node.type ? renderType(node.type, transformType) : void 0;
1669
- if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1670
- if (type) {
1671
- if (node.optional) return `${name}?: ${type}`;
1672
- return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
1673
- }
1674
- return node.default ? `${name} = ${node.default}` : name;
1675
- },
1676
- FunctionParameters(node) {
1677
- return sortParams(node.params).map((p) => this.transform(p)).filter(Boolean).join(", ");
1919
+ function functionPrinter(options) {
1920
+ return {
1921
+ name: "functionParameters",
1922
+ options,
1923
+ print(node) {
1924
+ return sortParams(node.params).map((p) => printParameter(p, options)).filter(Boolean).join(", ");
1925
+ }
1926
+ };
1927
+ }
1928
+ //#endregion
1929
+ //#region src/printers/operationParams.ts
1930
+ /**
1931
+ * Resolves the {@link TypeExpression} for an individual parameter.
1932
+ *
1933
+ * Without a resolver, it falls back to the schema primitive (a plain type-name string). When the
1934
+ * parameter belongs to a named group, it emits an `IndexedAccessType` like `GroupParams['petId']`,
1935
+ * otherwise the resolved individual name.
1936
+ */
1937
+ function resolveParamType({ node, param, resolver }) {
1938
+ if (!resolver) return param.schema.primitive ?? "unknown";
1939
+ const individualName = resolver.resolveParamName(node, param);
1940
+ const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
1941
+ const groupResolvers = {
1942
+ path: resolver.resolvePathParamsName,
1943
+ query: resolver.resolveQueryParamsName,
1944
+ header: resolver.resolveHeaderParamsName
1945
+ };
1946
+ const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
1947
+ if (groupName && groupName !== individualName) return createIndexedAccessType({
1948
+ target: groupName,
1949
+ key: param.name
1950
+ });
1951
+ return individualName;
1952
+ }
1953
+ /**
1954
+ * Derives a {@link ParamGroupType} for a query or header group from the resolver.
1955
+ *
1956
+ * Returns `null` when there is no resolver, no params, or the group name equals the
1957
+ * individual param name (so there is no real group to emit).
1958
+ */
1959
+ function resolveGroupType({ node, params, group, resolver }) {
1960
+ if (!resolver || !params.length) return null;
1961
+ const firstParam = params[0];
1962
+ const groupName = (group === "query" ? resolver.resolveQueryParamsName : resolver.resolveHeaderParamsName).call(resolver, node, firstParam);
1963
+ if (groupName === resolver.resolveParamName(node, firstParam)) return null;
1964
+ return {
1965
+ type: groupName,
1966
+ optional: params.every((p) => !p.required)
1967
+ };
1968
+ }
1969
+ /**
1970
+ * Converts an `OperationNode` into function parameters for code generation.
1971
+ *
1972
+ * Centralizes parameter grouping logic for all plugins. `paramsType` chooses between one
1973
+ * destructured object parameter (`object`) and separate top-level parameters (`inline`), while
1974
+ * `pathParamsType` controls how path params render in inline mode. Provide a `resolver` for type
1975
+ * name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
1976
+ */
1977
+ function createOperationParams(node, options) {
1978
+ const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
1979
+ const dataName = paramNames?.data ?? "data";
1980
+ const paramsName = paramNames?.params ?? "params";
1981
+ const headersName = paramNames?.headers ?? "headers";
1982
+ const pathName = paramNames?.path ?? "pathParams";
1983
+ const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
1984
+ const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
1985
+ const casedParams = caseParams(node.parameters, paramsCasing);
1986
+ const pathParams = casedParams.filter((p) => p.in === "path");
1987
+ const queryParams = casedParams.filter((p) => p.in === "query");
1988
+ const headerParams = casedParams.filter((p) => p.in === "header");
1989
+ const toProperty = (param) => ({
1990
+ name: param.name,
1991
+ type: wrapTypeExpression(resolveParamType({
1992
+ node,
1993
+ param,
1994
+ resolver
1995
+ })),
1996
+ optional: !param.required
1997
+ });
1998
+ const emptyObjectDefault = (props) => props.every((p) => p.optional) ? "{}" : void 0;
1999
+ const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.resolveDataName(node) ?? "unknown") : void 0;
2000
+ const bodyProperty = bodyType ? [{
2001
+ name: dataName,
2002
+ type: bodyType,
2003
+ optional: !(node.requestBody?.required ?? false)
2004
+ }] : [];
2005
+ const trailingGroups = [{
2006
+ name: paramsName,
2007
+ node,
2008
+ params: queryParams,
2009
+ groupType: resolveGroupType({
2010
+ node,
2011
+ params: queryParams,
2012
+ group: "query",
2013
+ resolver
2014
+ }),
2015
+ resolver,
2016
+ wrapType
2017
+ }, {
2018
+ name: headersName,
2019
+ node,
2020
+ params: headerParams,
2021
+ groupType: resolveGroupType({
2022
+ node,
2023
+ params: headerParams,
2024
+ group: "header",
2025
+ resolver
2026
+ }),
2027
+ resolver,
2028
+ wrapType
2029
+ }];
2030
+ const params = [];
2031
+ if (paramsType === "object") {
2032
+ const children = [
2033
+ ...pathParams.map(toProperty),
2034
+ ...bodyProperty,
2035
+ ...trailingGroups.flatMap(buildGroupProperty)
2036
+ ];
2037
+ if (children.length) params.push(createFunctionParameter({
2038
+ properties: children,
2039
+ default: emptyObjectDefault(children)
2040
+ }));
2041
+ } else {
2042
+ if (pathParamsType === "inlineSpread" && pathParams.length) {
2043
+ const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]);
2044
+ params.push(createFunctionParameter({
2045
+ name: pathName,
2046
+ type: spreadType ? wrapType(spreadType) : void 0,
2047
+ rest: true
2048
+ }));
2049
+ } else if (pathParamsType === "inline") params.push(...pathParams.map((p) => createFunctionParameter(toProperty(p))));
2050
+ else if (pathParams.length) {
2051
+ const pathChildren = pathParams.map(toProperty);
2052
+ params.push(createFunctionParameter({
2053
+ properties: pathChildren,
2054
+ default: pathParamsDefault ?? emptyObjectDefault(pathChildren)
2055
+ }));
1678
2056
  }
2057
+ params.push(...bodyProperty.map((p) => createFunctionParameter(p)));
2058
+ params.push(...trailingGroups.flatMap(buildGroupParam));
1679
2059
  }
1680
- }));
2060
+ params.push(...extraParams);
2061
+ return createFunctionParameters({ params });
2062
+ }
2063
+ /**
2064
+ * Builds the property descriptor for a query or header group.
2065
+ * Returns an empty array when there are no params to emit.
2066
+ *
2067
+ * A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
2068
+ * {@link TypeLiteralNode} from the individual params.
2069
+ */
2070
+ function buildGroupProperty({ name, node, params, groupType, resolver, wrapType }) {
2071
+ if (groupType) return [{
2072
+ name,
2073
+ type: typeof groupType.type === "string" ? wrapType(groupType.type) : groupType.type,
2074
+ optional: groupType.optional
2075
+ }];
2076
+ if (params.length) return [{
2077
+ name,
2078
+ type: buildTypeLiteral({
2079
+ node,
2080
+ params,
2081
+ resolver
2082
+ }),
2083
+ optional: params.every((p) => !p.required)
2084
+ }];
2085
+ return [];
2086
+ }
2087
+ /**
2088
+ * Builds a single {@link FunctionParameterNode} for a query or header group.
2089
+ * Returns an empty array when there are no params to emit.
2090
+ */
2091
+ function buildGroupParam(args) {
2092
+ return buildGroupProperty(args).map((p) => createFunctionParameter(p));
2093
+ }
2094
+ /**
2095
+ * Builds a {@link TypeLiteralNode} for an inline anonymous type grouping named fields.
2096
+ *
2097
+ * Used when query or header parameters have no dedicated group type name.
2098
+ */
2099
+ function buildTypeLiteral({ node, params, resolver }) {
2100
+ return createTypeLiteral({ members: params.map((p) => ({
2101
+ name: p.name,
2102
+ type: resolveParamType({
2103
+ node,
2104
+ param: p,
2105
+ resolver
2106
+ }),
2107
+ optional: !p.required
2108
+ })) });
2109
+ }
1681
2110
  //#endregion
1682
- export { Enum, Type, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
2111
+ export { Enum, Type, createFunctionParameter, createFunctionParameters, createIndexedAccessType, createObjectBindingPattern, createOperationParams, createTypeLiteral, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
1683
2112
 
1684
2113
  //# sourceMappingURL=index.js.map