@kubb/plugin-ts 5.0.0-beta.94 → 5.0.0-beta.98

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
@@ -106,31 +106,6 @@ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
106
106
  }).toUpperCase();
107
107
  }
108
108
  //#endregion
109
- //#region ../../internals/utils/src/fs.ts
110
- /**
111
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
112
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
113
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
114
- *
115
- * Empty segments are dropped before joining. They arise when the name starts with a dot
116
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
117
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
118
- * absolute path, letting generated files escape the configured output directory.
119
- *
120
- * @example Nested path from a dotted name
121
- * `toFilePath('pet.petId') // 'pet/petId'`
122
- *
123
- * @example PascalCase the final segment
124
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
125
- *
126
- * @example Suffix applied to the final segment only
127
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
128
- */
129
- function toFilePath(name, caseLast = camelCase) {
130
- const parts = name.split(/\.(?=[a-zA-Z])/);
131
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
132
- }
133
- //#endregion
134
109
  //#region ../../internals/utils/src/reserved.ts
135
110
  /**
136
111
  * JavaScript and Java reserved words.
@@ -331,6 +306,31 @@ function jsStringEscape(input) {
331
306
  });
332
307
  }
333
308
  //#endregion
309
+ //#region ../../internals/utils/src/fs.ts
310
+ /**
311
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
312
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
313
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
314
+ *
315
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
316
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
317
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
318
+ * absolute path, letting generated files escape the configured output directory.
319
+ *
320
+ * @example Nested path from a dotted name
321
+ * `toFilePath('pet.petId') // 'pet/petId'`
322
+ *
323
+ * @example PascalCase the final segment
324
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
325
+ *
326
+ * @example Suffix applied to the final segment only
327
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
328
+ */
329
+ function toFilePath(name, caseLast = camelCase) {
330
+ const parts = name.split(/\.(?=[a-zA-Z])/);
331
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
332
+ }
333
+ //#endregion
334
334
  //#region src/constants.ts
335
335
  /**
336
336
  * `optionalType` values that cause a property's type to include `| undefined`.
@@ -374,7 +374,6 @@ const PARAM_RANK = {
374
374
  //#endregion
375
375
  //#region src/factory.ts
376
376
  var factory_exports = /* @__PURE__ */ __exportAll({
377
- SyntaxKind: () => SyntaxKind,
378
377
  appendJSDocToNode: () => appendJSDocToNode,
379
378
  buildIndexSignatures: () => buildIndexSignatures,
380
379
  buildMemberNodes: () => buildMemberNodes,
@@ -382,29 +381,15 @@ var factory_exports = /* @__PURE__ */ __exportAll({
382
381
  buildTupleNode: () => buildTupleNode,
383
382
  constToTypeNode: () => constToTypeNode,
384
383
  createArrayDeclaration: () => createArrayDeclaration,
385
- createArrayTypeNode: () => createArrayTypeNode,
386
384
  createEnumDeclaration: () => createEnumDeclaration,
387
385
  createExportDeclaration: () => createExportDeclaration,
388
- createFalse: () => createFalse,
389
- createIdentifier: () => createIdentifier,
390
386
  createImportDeclaration: () => createImportDeclaration,
391
- createIndexSignature: () => createIndexSignature,
392
- createInterfaceDeclaration: () => createInterfaceDeclaration,
393
387
  createIntersectionDeclaration: () => createIntersectionDeclaration,
394
388
  createJSDoc: () => createJSDoc,
395
- createLiteralTypeNode: () => createLiteralTypeNode,
396
- createNumericLiteral: () => createNumericLiteral,
397
389
  createOmitDeclaration: () => createOmitDeclaration,
398
- createOptionalTypeNode: () => createOptionalTypeNode,
399
390
  createParameterSignature: () => createParameterSignature,
400
- createPrefixUnaryExpression: () => createPrefixUnaryExpression,
401
391
  createPropertySignature: () => createPropertySignature,
402
392
  createQuestionToken: () => createQuestionToken,
403
- createRestTypeNode: () => createRestTypeNode,
404
- createStringLiteral: () => createStringLiteral,
405
- createTrue: () => createTrue,
406
- createTupleTypeNode: () => createTupleTypeNode,
407
- createTypeAliasDeclaration: () => createTypeAliasDeclaration,
408
393
  createTypeDeclaration: () => createTypeDeclaration,
409
394
  createTypeLiteralNode: () => createTypeLiteralNode,
410
395
  createTypeReferenceNode: () => createTypeReferenceNode,
@@ -1062,22 +1047,93 @@ function getOperationParameters(node, options = {}) {
1062
1047
  };
1063
1048
  }
1064
1049
  //#endregion
1065
- //#region ../../internals/shared/src/adapter.ts
1050
+ //#region ../../internals/shared/src/resolver.ts
1066
1051
  /**
1067
- * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
1068
- * so OAS-only options (`dateType`, `nameMapping`) and the parsed `document` are typed.
1052
+ * Resolves a single operation parameter name with the
1053
+ * `<operationId> <in> <name>` template.
1069
1054
  *
1070
- * Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
1071
- * clear, actionable error at the point of use.
1055
+ * @example
1056
+ * `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
1057
+ */
1058
+ function operationParamName(node, param) {
1059
+ return this.name(`${node.operationId} ${param.in} ${param.name}`);
1060
+ }
1061
+ /**
1062
+ * Builds the shared `param` namespace. Spread the result into `createResolver`
1063
+ * and override individual methods next to it when a plugin deviates.
1072
1064
  *
1073
1065
  * @example
1074
1066
  * ```ts
1075
- * const { dateType } = getOasAdapter(ctx.adapter).options
1067
+ * createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
1076
1068
  * ```
1077
1069
  */
1078
- function getOasAdapter(adapter) {
1079
- if (adapter.name !== "oas") throw new Error(`Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}". Configure \`adapter: adapterOas()\` in your Kubb config.`);
1080
- return adapter;
1070
+ function createOperationParamResolver() {
1071
+ return {
1072
+ name: operationParamName,
1073
+ path(node) {
1074
+ return this.name(`${node.operationId} Path`);
1075
+ },
1076
+ query(node) {
1077
+ return this.name(`${node.operationId} Query`);
1078
+ },
1079
+ headers(node) {
1080
+ return this.name(`${node.operationId} Headers`);
1081
+ }
1082
+ };
1083
+ }
1084
+ /**
1085
+ * Builds the shared `response` namespace. Spread the result into
1086
+ * `createResolver` and add plugin-specific methods (`options`, `error`) next
1087
+ * to it.
1088
+ *
1089
+ * @example
1090
+ * ```ts
1091
+ * createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })
1092
+ * ```
1093
+ */
1094
+ function createOperationResponseResolver() {
1095
+ return {
1096
+ status(node, statusCode) {
1097
+ return this.name(`${node.operationId} Status ${statusCode}`);
1098
+ },
1099
+ body(node) {
1100
+ return this.name(`${node.operationId} Body`);
1101
+ },
1102
+ responses(node) {
1103
+ return this.name(`${node.operationId} Responses`);
1104
+ },
1105
+ response(node) {
1106
+ return this.name(`${node.operationId} Response`);
1107
+ }
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Builds a resolver `file` override whose base name runs every path segment
1112
+ * through `toFilePath`, casing the final segment with `caseLast`.
1113
+ *
1114
+ * @example
1115
+ * ```ts
1116
+ * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })
1117
+ * ```
1118
+ */
1119
+ function createCasedFile(caseLast) {
1120
+ return { baseName({ name, extname }) {
1121
+ return `${toFilePath(name, caseLast)}${extname}`;
1122
+ } };
1123
+ }
1124
+ //#endregion
1125
+ //#region ../../internals/shared/src/refs.ts
1126
+ /**
1127
+ * Collects the resolved target name of every pointer-carrying ref in a schema tree, in
1128
+ * first-occurrence order. Use this for name-only checks (e.g. redeclaration detection) where
1129
+ * `resolver.imports` would resolve file paths that are then discarded.
1130
+ */
1131
+ function collectRefNames(schema) {
1132
+ return kubb_kit.ast.collect(schema, { schema: (node) => {
1133
+ const refNode = kubb_kit.ast.narrowSchema(node, "ref");
1134
+ if (!refNode?.ref) return null;
1135
+ return kubb_kit.ast.resolveRefName(refNode);
1136
+ } });
1081
1137
  }
1082
1138
  //#endregion
1083
1139
  //#region ../../internals/shared/src/group.ts
@@ -1134,6 +1190,10 @@ function isInlineConstEnum(node, enumSchemaNames) {
1134
1190
  function isSchemaOptional(schema) {
1135
1191
  return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
1136
1192
  }
1193
+ function formatExample(value) {
1194
+ if (value === null || typeof value !== "object") return String(value);
1195
+ return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
1196
+ }
1137
1197
  function buildPropertyJSDocComments(schema, optional) {
1138
1198
  const meta = kubb_kit.ast.syncSchemaRef(schema);
1139
1199
  const isArray = meta?.primitive === "array";
@@ -1148,7 +1208,7 @@ function buildPropertyJSDocComments(schema, optional) {
1148
1208
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
1149
1209
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
1150
1210
  meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" && typeof meta.default === "string" ? stringify(meta.default) : meta.default}` : null,
1151
- ...exampleValues.map((example) => `@example ${example}`),
1211
+ ...exampleValues.map((example) => `@example ${formatExample(example)}`),
1152
1212
  meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
1153
1213
  ].filter(Boolean);
1154
1214
  }
@@ -1379,8 +1439,10 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
1379
1439
  time: dateOrStringNode,
1380
1440
  ref(node) {
1381
1441
  if (!node.name) return null;
1382
- const refName = node.ref ? this.options.nameMapping?.get(node.ref) ?? kubb_kit.ast.extractRefName(node.ref) ?? node.name : node.name;
1383
- return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.enum.keyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.name(refName) : refName, void 0);
1442
+ const refName = kubb_kit.ast.resolveRefName(node);
1443
+ if (!refName) return null;
1444
+ const name = node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.enum.keyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.name(refName) : refName;
1445
+ return createTypeReferenceNode(name, void 0);
1384
1446
  },
1385
1447
  enum(node) {
1386
1448
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
@@ -1388,7 +1450,8 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
1388
1450
  withParentheses: true,
1389
1451
  nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
1390
1452
  }) ?? void 0;
1391
- return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.enum.keyName(node, this.options.enum.typeSuffix) : this.options.resolver.name(node.name), void 0);
1453
+ const resolvedName = ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.enum.keyName(node, this.options.enum.typeSuffix) : this.options.resolver.name(node.name);
1454
+ return createTypeReferenceNode(resolvedName, void 0);
1392
1455
  },
1393
1456
  union(node) {
1394
1457
  const members = node.members ?? [];
@@ -1505,24 +1568,22 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1505
1568
  renderer: kubb_jsx.jsxRenderer,
1506
1569
  schema(node, ctx) {
1507
1570
  const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
1508
- const { adapter, config, resolver, root } = ctx;
1571
+ const { config, resolver, root } = ctx;
1509
1572
  if (!node.name) return;
1510
1573
  const enumSchemaNames = new Set(ctx.meta.enumNames);
1511
- const imports = adapter.getImports(node, (schemaName) => ({
1512
- name: resolveImportName({
1513
- schemaName,
1514
- enumOptions,
1515
- enumSchemaNames,
1516
- resolver
1517
- }),
1518
- path: resolver.file({
1519
- name: schemaName,
1520
- extname: ".ts",
1521
- root,
1522
- output,
1523
- group: group ?? void 0
1524
- }).path
1525
- }));
1574
+ const importName = (schemaName) => resolveImportName({
1575
+ schemaName,
1576
+ enumOptions,
1577
+ enumSchemaNames,
1578
+ resolver
1579
+ });
1580
+ const imports = resolver.imports({
1581
+ node,
1582
+ root,
1583
+ output,
1584
+ group: group ?? void 0,
1585
+ name: importName
1586
+ });
1526
1587
  const enumNode = kubb_kit.ast.narrowSchema(node, kubb_kit.ast.schemaTypes.enum);
1527
1588
  const isEnumSchema = !!enumNode && !isInlineConstEnum(enumNode, enumSchemaNames);
1528
1589
  const meta = {
@@ -1544,7 +1605,6 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1544
1605
  description: node.description,
1545
1606
  resolver,
1546
1607
  enumSchemaNames,
1547
- nameMapping: getOasAdapter(adapter).options.nameMapping,
1548
1608
  nodes: printer?.nodes
1549
1609
  });
1550
1610
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
@@ -1587,7 +1647,7 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1587
1647
  },
1588
1648
  operation(node, ctx) {
1589
1649
  const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
1590
- const { adapter, config, resolver, root } = ctx;
1650
+ const { config, resolver, root } = ctx;
1591
1651
  const params = caseParams(node.parameters, "camelcase");
1592
1652
  const meta = { file: resolver.file({
1593
1653
  name: node.operationId,
@@ -1599,23 +1659,21 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1599
1659
  group: group ?? void 0
1600
1660
  }) };
1601
1661
  const enumSchemaNames = new Set(ctx.meta.enumNames);
1662
+ const importName = (schemaName) => resolveImportName({
1663
+ schemaName,
1664
+ enumOptions,
1665
+ enumSchemaNames,
1666
+ resolver
1667
+ });
1602
1668
  function renderSchemaType({ schema, name, keysToOmit }) {
1603
1669
  if (!schema) return null;
1604
- const imports = adapter.getImports(schema, (schemaName) => ({
1605
- name: resolveImportName({
1606
- schemaName,
1607
- enumOptions,
1608
- enumSchemaNames,
1609
- resolver
1610
- }),
1611
- path: resolver.file({
1612
- name: schemaName,
1613
- extname: ".ts",
1614
- root,
1615
- output,
1616
- group: group ?? void 0
1617
- }).path
1618
- }));
1670
+ const imports = resolver.imports({
1671
+ node: schema,
1672
+ root,
1673
+ output,
1674
+ group: group ?? void 0,
1675
+ name: importName
1676
+ });
1619
1677
  const schemaPrinter = printerTs({
1620
1678
  optionalType,
1621
1679
  arrayType,
@@ -1626,7 +1684,6 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1626
1684
  keysToOmit,
1627
1685
  resolver,
1628
1686
  enumSchemaNames,
1629
- nameMapping: getOasAdapter(adapter).options.nameMapping,
1630
1687
  nodes: printer?.nodes
1631
1688
  });
1632
1689
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
@@ -1735,15 +1792,7 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1735
1792
  if (!node.responses.some(hasSchema)) return null;
1736
1793
  const responseName = resolver.response.response(node);
1737
1794
  const responsesWithSchema = node.responses.filter(hasSchema);
1738
- if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
1739
- name: resolveImportName({
1740
- schemaName,
1741
- enumOptions,
1742
- enumSchemaNames,
1743
- resolver
1744
- }),
1745
- path: ""
1746
- })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseName)) return null;
1795
+ if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? collectRefNames(entry.schema).map(importName) : []))).has(responseName)) return null;
1747
1796
  return renderSchemaType({
1748
1797
  schema: {
1749
1798
  ...buildResponseUnion(node, { resolver }),
@@ -1810,38 +1859,12 @@ const resolverTs = (0, kubb_kit.createResolver)({
1810
1859
  name(name) {
1811
1860
  return ensureValidVarName(pascalCase(name));
1812
1861
  },
1813
- file: { baseName({ name, extname }) {
1814
- return `${toFilePath(name, pascalCase)}${extname}`;
1815
- } },
1816
- param: {
1817
- name(node, param) {
1818
- return this.name(`${node.operationId} ${param.in} ${param.name}`);
1819
- },
1820
- path(node) {
1821
- return this.name(`${node.operationId} Path`);
1822
- },
1823
- query(node) {
1824
- return this.name(`${node.operationId} Query`);
1825
- },
1826
- headers(node) {
1827
- return this.name(`${node.operationId} Headers`);
1828
- }
1829
- },
1862
+ file: createCasedFile(pascalCase),
1863
+ param: createOperationParamResolver(),
1830
1864
  response: {
1831
- status(node, statusCode) {
1832
- return this.name(`${node.operationId} Status ${statusCode}`);
1833
- },
1865
+ ...createOperationResponseResolver(),
1834
1866
  options(node) {
1835
1867
  return this.name(`${node.operationId} Options`);
1836
- },
1837
- responses(node) {
1838
- return this.name(`${node.operationId} Responses`);
1839
- },
1840
- response(node) {
1841
- return this.name(`${node.operationId} Response`);
1842
- },
1843
- body(node) {
1844
- return this.name(`${node.operationId} Body`);
1845
1868
  }
1846
1869
  },
1847
1870
  enum: { keyName(node, enumTypeSuffix = "key") {