@kubb/plugin-ts 5.0.0-beta.95 → 5.0.0-beta.99

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.d.ts CHANGED
@@ -2,6 +2,7 @@ import { n as __name } from "./rolldown-runtime-CNktS9qV.js";
2
2
  import { KubbReactNode } from "kubb/jsx";
3
3
  import { Exclude, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ResolverPatch, ast } from "kubb/kit";
4
4
  import ts from "typescript";
5
+ import "@kubb/adapter-oas";
5
6
  //#region src/printers/printerTs.d.ts
6
7
  /**
7
8
  * Partial map of node-type overrides for the TypeScript printer.
@@ -445,16 +446,6 @@ type Props$1 = {
445
446
  resolver: ResolverTs;
446
447
  key?: string | number | null;
447
448
  };
448
- /**
449
- * Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
450
- *
451
- * The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
452
- * valid TypeScript identifier. The resolver normalizes it. For inline enum properties the adapter
453
- * already emits a PascalCase+suffix name, so resolution is typically a no-op.
454
- *
455
- * When `constCasing` is `'pascalCase'` and `typeSuffix` is empty, the const and the type
456
- * resolve to the same name, which TypeScript merges into a single value+type declaration.
457
- */
458
449
  /**
459
450
  * Renders the enum declaration(s) for a single named `EnumSchemaNode`.
460
451
  *
@@ -466,11 +457,7 @@ type Props$1 = {
466
457
  * The emitted `File.Source` nodes carry the resolved names so that the barrel
467
458
  * index picks up the correct export identifiers.
468
459
  */
469
- declare function Enum({
470
- node,
471
- enum: enumOptions,
472
- resolver
473
- }: Props$1): KubbReactNode;
460
+ declare function Enum({ node, enum: enumOptions, resolver }: Props$1): KubbReactNode;
474
461
  //#endregion
475
462
  //#region src/components/Type.d.ts
476
463
  type Props = {
@@ -484,13 +471,7 @@ type Props = {
484
471
  enum: PluginTs['resolvedOptions']['enum'];
485
472
  resolver: ResolverTs;
486
473
  };
487
- declare function Type({
488
- name,
489
- node,
490
- printer,
491
- enum: enumOptions,
492
- resolver
493
- }: Props): KubbReactNode;
474
+ declare function Type({ name, node, printer, enum: enumOptions, resolver }: Props): KubbReactNode;
494
475
  //#endregion
495
476
  //#region src/generators/typeGenerator.d.ts
496
477
  /**
@@ -864,10 +845,6 @@ type CreateOperationParamsOptions = {
864
845
  * - `'inlineSpread'` emits a single rest parameter `...pathParams: PathParams`
865
846
  */
866
847
  pathParamsType: 'object' | 'inline' | 'inlineSpread';
867
- /**
868
- * Converts parameter names to camelCase before output.
869
- */
870
- paramsCasing?: 'camelcase';
871
848
  /**
872
849
  * Resolver for parameter and request body type names.
873
850
  * Pass `ResolverTs` from `@kubb/plugin-ts` directly.
@@ -967,9 +944,7 @@ type BuildParamsSchemaOptions = {
967
944
  * header), embedding each param's own schema (and JSDoc) directly rather than referencing a
968
945
  * separate per-param type — the group itself is the only type these params get exported as.
969
946
  */
970
- declare function buildParams({
971
- params
972
- }: BuildParamsSchemaOptions): ast.SchemaNode;
947
+ declare function buildParams({ params }: BuildParamsSchemaOptions): ast.SchemaNode;
973
948
  //#endregion
974
949
  export { type CreateOperationParamsOptions, Enum, type FunctionParameterNode, type FunctionParametersNode, type IndexedAccessTypeNode, type ObjectBindingPatternNode, type PluginTs, type PrinterTsFactory, type PrinterTsNodes, type PrinterTsOptions, type ResolverTs, Type, type TypeExpression, type TypeLiteralNode, buildParams, createFunctionParameter, createFunctionParameters, createIndexedAccessType, createObjectBindingPattern, createOperationParams, createTypeLiteral, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
975
950
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -67,31 +67,6 @@ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
67
67
  }).toUpperCase();
68
68
  }
69
69
  //#endregion
70
- //#region ../../internals/utils/src/fs.ts
71
- /**
72
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
73
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
74
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
75
- *
76
- * Empty segments are dropped before joining. They arise when the name starts with a dot
77
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
78
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
79
- * absolute path, letting generated files escape the configured output directory.
80
- *
81
- * @example Nested path from a dotted name
82
- * `toFilePath('pet.petId') // 'pet/petId'`
83
- *
84
- * @example PascalCase the final segment
85
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
86
- *
87
- * @example Suffix applied to the final segment only
88
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
89
- */
90
- function toFilePath(name, caseLast = camelCase) {
91
- const parts = name.split(/\.(?=[a-zA-Z])/);
92
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
93
- }
94
- //#endregion
95
70
  //#region ../../internals/utils/src/reserved.ts
96
71
  /**
97
72
  * JavaScript and Java reserved words.
@@ -292,6 +267,31 @@ function jsStringEscape(input) {
292
267
  });
293
268
  }
294
269
  //#endregion
270
+ //#region ../../internals/utils/src/fs.ts
271
+ /**
272
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
273
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
274
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
275
+ *
276
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
277
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
278
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
279
+ * absolute path, letting generated files escape the configured output directory.
280
+ *
281
+ * @example Nested path from a dotted name
282
+ * `toFilePath('pet.petId') // 'pet/petId'`
283
+ *
284
+ * @example PascalCase the final segment
285
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
286
+ *
287
+ * @example Suffix applied to the final segment only
288
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
289
+ */
290
+ function toFilePath(name, caseLast = camelCase) {
291
+ const parts = name.split(/\.(?=[a-zA-Z])/);
292
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
293
+ }
294
+ //#endregion
295
295
  //#region src/constants.ts
296
296
  /**
297
297
  * `optionalType` values that cause a property's type to include `| undefined`.
@@ -912,39 +912,19 @@ function Enum({ node, enum: enumOptions, resolver }) {
912
912
  }
913
913
  //#endregion
914
914
  //#region ../../internals/shared/src/params.ts
915
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
916
915
  /**
917
- * Applies camelCase to parameter names and returns a new array without mutating the input.
916
+ * Drops parameters that share the same name, keeping the first.
918
917
  *
919
- * Run it before handing parameters to schema builders so output property keys get the right casing
920
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
921
- * original array is returned unchanged. Results are cached per input array.
918
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
919
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
920
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
921
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
922
922
  */
923
- function caseParams(params, casing) {
924
- if (!casing) return params;
925
- const cached = caseParamsCache.get(params);
926
- if (cached) return cached;
927
- const result = params.map((param) => ({
928
- ...param,
929
- name: camelCase(param.name)
930
- }));
931
- caseParamsCache.set(params, result);
932
- return result;
933
- }
934
- /**
935
- * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
936
- *
937
- * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
938
- * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
939
- * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
940
- * identity so the resulting group is collision-free regardless of the names each caller carries.
941
- */
942
- function dedupeByCasedName(params) {
923
+ function dedupeParams(params) {
943
924
  const seen = /* @__PURE__ */ new Set();
944
925
  return params.filter((param) => {
945
- const key = camelCase(param.name);
946
- if (seen.has(key)) return false;
947
- seen.add(key);
926
+ if (seen.has(param.name)) return false;
927
+ seen.add(param.name);
948
928
  return true;
949
929
  });
950
930
  }
@@ -998,13 +978,15 @@ function resolveContentTypeVariants(entries, baseName) {
998
978
  };
999
979
  });
1000
980
  }
1001
- function getOperationParameters(node, options = {}) {
1002
- const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
981
+ function getOperationParameters(node) {
1003
982
  return {
1004
- path: dedupeByCasedName(params.filter((param) => param.in === "path")),
1005
- query: dedupeByCasedName(params.filter((param) => param.in === "query")),
1006
- header: dedupeByCasedName(params.filter((param) => param.in === "header")),
1007
- cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
983
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path").map((param) => isValidVarName(param.name) ? param : {
984
+ ...param,
985
+ name: camelCase(param.name)
986
+ })),
987
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
988
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
989
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
1008
990
  };
1009
991
  }
1010
992
  //#endregion
@@ -1128,6 +1110,35 @@ function createGroupConfig(group) {
1128
1110
  };
1129
1111
  }
1130
1112
  //#endregion
1113
+ //#region ../../internals/shared/src/schemaTraversal.ts
1114
+ /**
1115
+ * Maps each property of an object schema to its transformed output. Pairs every result with the
1116
+ * original property so the printer keeps full control over modifiers, getters, and key syntax.
1117
+ *
1118
+ * @example
1119
+ * ```ts
1120
+ * const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
1121
+ * // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
1122
+ * ```
1123
+ */
1124
+ function mapSchemaProperties(node, transform) {
1125
+ return node.properties.map((property) => ({
1126
+ name: property.name,
1127
+ property,
1128
+ output: transform(property.schema)
1129
+ }));
1130
+ }
1131
+ /**
1132
+ * Maps each item of an array or tuple schema to its transformed output, pairing every result with
1133
+ * the original item.
1134
+ */
1135
+ function mapSchemaItems(node, transform) {
1136
+ return (node.items ?? []).map((schema) => ({
1137
+ schema,
1138
+ output: transform(schema)
1139
+ }));
1140
+ }
1141
+ //#endregion
1131
1142
  //#region src/utils.ts
1132
1143
  /**
1133
1144
  * Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
@@ -1151,6 +1162,10 @@ function isInlineConstEnum(node, enumSchemaNames) {
1151
1162
  function isSchemaOptional(schema) {
1152
1163
  return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
1153
1164
  }
1165
+ function formatExample(value) {
1166
+ if (value === null || typeof value !== "object") return String(value);
1167
+ return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
1168
+ }
1154
1169
  function buildPropertyJSDocComments(schema, optional) {
1155
1170
  const meta = ast.syncSchemaRef(schema);
1156
1171
  const isArray = meta?.primitive === "array";
@@ -1165,7 +1180,7 @@ function buildPropertyJSDocComments(schema, optional) {
1165
1180
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
1166
1181
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
1167
1182
  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,
1168
- ...exampleValues.map((example) => `@example ${example}`),
1183
+ ...exampleValues.map((example) => `@example ${formatExample(example)}`),
1169
1184
  meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
1170
1185
  ].filter(Boolean);
1171
1186
  }
@@ -1188,7 +1203,7 @@ function buildParams({ params }) {
1188
1203
  });
1189
1204
  }
1190
1205
  function buildOptions(node, { resolver }) {
1191
- const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing: "original" });
1206
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
1192
1207
  const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
1193
1208
  const hasRequiredPath = pathParams.some((param) => param.required);
1194
1209
  const hasRequiredQuery = queryParams.some((param) => param.required);
@@ -1398,7 +1413,8 @@ const printerTs = ast.createPrinter((options) => {
1398
1413
  if (!node.name) return null;
1399
1414
  const refName = ast.resolveRefName(node);
1400
1415
  if (!refName) return null;
1401
- 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);
1416
+ 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;
1417
+ return createTypeReferenceNode(name, void 0);
1402
1418
  },
1403
1419
  enum(node) {
1404
1420
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
@@ -1406,7 +1422,8 @@ const printerTs = ast.createPrinter((options) => {
1406
1422
  withParentheses: true,
1407
1423
  nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
1408
1424
  }) ?? void 0;
1409
- 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);
1425
+ 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);
1426
+ return createTypeReferenceNode(resolvedName, void 0);
1410
1427
  },
1411
1428
  union(node) {
1412
1429
  const members = node.members ?? [];
@@ -1437,7 +1454,7 @@ const printerTs = ast.createPrinter((options) => {
1437
1454
  },
1438
1455
  array(node) {
1439
1456
  return createArrayDeclaration({
1440
- nodes: ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
1457
+ nodes: mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
1441
1458
  arrayType: this.options.arrayType
1442
1459
  }) ?? null;
1443
1460
  },
@@ -1447,7 +1464,7 @@ const printerTs = ast.createPrinter((options) => {
1447
1464
  object(node) {
1448
1465
  const { transform, options } = this;
1449
1466
  const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
1450
- const propertyNodes = ast.mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
1467
+ const propertyNodes = mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
1451
1468
  const baseType = output ?? keywordTypeNodes.unknown;
1452
1469
  const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
1453
1470
  const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
@@ -1603,7 +1620,6 @@ const typeGenerator = defineGenerator({
1603
1620
  operation(node, ctx) {
1604
1621
  const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
1605
1622
  const { config, resolver, root } = ctx;
1606
- const params = caseParams(node.parameters, "camelcase");
1607
1623
  const meta = { file: resolver.file({
1608
1624
  name: node.operationId,
1609
1625
  extname: ".ts",
@@ -1682,10 +1698,7 @@ const typeGenerator = defineGenerator({
1682
1698
  name: baseName
1683
1699
  })] });
1684
1700
  }
1685
- const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters({
1686
- ...node,
1687
- parameters: params
1688
- }, { paramsCasing: "original" });
1701
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
1689
1702
  const paramGroupTypes = [
1690
1703
  pathParams.length > 0 && renderSchemaType({
1691
1704
  schema: buildParams({ params: pathParams }),
@@ -1732,10 +1745,7 @@ const typeGenerator = defineGenerator({
1732
1745
  });
1733
1746
  });
1734
1747
  const optionsType = renderSchemaType({
1735
- schema: buildOptions({
1736
- ...node,
1737
- parameters: params
1738
- }, { resolver }),
1748
+ schema: buildOptions(node, { resolver }),
1739
1749
  name: resolver.response.options(node)
1740
1750
  });
1741
1751
  const responsesType = renderSchemaType({
@@ -2185,17 +2195,14 @@ function resolveGroupType({ node, params, group, resolver }) {
2185
2195
  * name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
2186
2196
  */
2187
2197
  function createOperationParams(node, options) {
2188
- const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
2198
+ const { paramsType, pathParamsType, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
2189
2199
  const dataName = paramNames?.data ?? "data";
2190
2200
  const paramsName = paramNames?.params ?? "params";
2191
2201
  const headersName = paramNames?.headers ?? "headers";
2192
2202
  const pathName = paramNames?.path ?? "pathParams";
2193
2203
  const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
2194
2204
  const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
2195
- const casedParams = caseParams(node.parameters, paramsCasing);
2196
- const pathParams = casedParams.filter((p) => p.in === "path");
2197
- const queryParams = casedParams.filter((p) => p.in === "query");
2198
- const headerParams = casedParams.filter((p) => p.in === "header");
2205
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
2199
2206
  const toProperty = (param) => ({
2200
2207
  name: param.name,
2201
2208
  type: wrapTypeExpression(resolveParamType({