@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.cjs +86 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -29
- package/dist/index.js +86 -79
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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`.
|
|
@@ -951,39 +951,19 @@ function Enum({ node, enum: enumOptions, resolver }) {
|
|
|
951
951
|
}
|
|
952
952
|
//#endregion
|
|
953
953
|
//#region ../../internals/shared/src/params.ts
|
|
954
|
-
const caseParamsCache = /* @__PURE__ */ new WeakMap();
|
|
955
954
|
/**
|
|
956
|
-
*
|
|
955
|
+
* Drops parameters that share the same name, keeping the first.
|
|
957
956
|
*
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
957
|
+
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
958
|
+
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
959
|
+
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
960
|
+
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
961
961
|
*/
|
|
962
|
-
function
|
|
963
|
-
if (!casing) return params;
|
|
964
|
-
const cached = caseParamsCache.get(params);
|
|
965
|
-
if (cached) return cached;
|
|
966
|
-
const result = params.map((param) => ({
|
|
967
|
-
...param,
|
|
968
|
-
name: camelCase(param.name)
|
|
969
|
-
}));
|
|
970
|
-
caseParamsCache.set(params, result);
|
|
971
|
-
return result;
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
|
-
* Drops parameters that collapse to the same property identity once camelCased, keeping the first.
|
|
975
|
-
*
|
|
976
|
-
* Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
|
|
977
|
-
* `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
|
|
978
|
-
* an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
|
|
979
|
-
* identity so the resulting group is collision-free regardless of the names each caller carries.
|
|
980
|
-
*/
|
|
981
|
-
function dedupeByCasedName(params) {
|
|
962
|
+
function dedupeParams(params) {
|
|
982
963
|
const seen = /* @__PURE__ */ new Set();
|
|
983
964
|
return params.filter((param) => {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
seen.add(key);
|
|
965
|
+
if (seen.has(param.name)) return false;
|
|
966
|
+
seen.add(param.name);
|
|
987
967
|
return true;
|
|
988
968
|
});
|
|
989
969
|
}
|
|
@@ -1037,13 +1017,15 @@ function resolveContentTypeVariants(entries, baseName) {
|
|
|
1037
1017
|
};
|
|
1038
1018
|
});
|
|
1039
1019
|
}
|
|
1040
|
-
function getOperationParameters(node
|
|
1041
|
-
const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
|
|
1020
|
+
function getOperationParameters(node) {
|
|
1042
1021
|
return {
|
|
1043
|
-
path:
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1022
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path").map((param) => isValidVarName(param.name) ? param : {
|
|
1023
|
+
...param,
|
|
1024
|
+
name: camelCase(param.name)
|
|
1025
|
+
})),
|
|
1026
|
+
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
1027
|
+
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
1028
|
+
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
1047
1029
|
};
|
|
1048
1030
|
}
|
|
1049
1031
|
//#endregion
|
|
@@ -1167,6 +1149,35 @@ function createGroupConfig(group) {
|
|
|
1167
1149
|
};
|
|
1168
1150
|
}
|
|
1169
1151
|
//#endregion
|
|
1152
|
+
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
1153
|
+
/**
|
|
1154
|
+
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
1155
|
+
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
1156
|
+
*
|
|
1157
|
+
* @example
|
|
1158
|
+
* ```ts
|
|
1159
|
+
* const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
|
|
1160
|
+
* // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
|
|
1161
|
+
* ```
|
|
1162
|
+
*/
|
|
1163
|
+
function mapSchemaProperties(node, transform) {
|
|
1164
|
+
return node.properties.map((property) => ({
|
|
1165
|
+
name: property.name,
|
|
1166
|
+
property,
|
|
1167
|
+
output: transform(property.schema)
|
|
1168
|
+
}));
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
1172
|
+
* the original item.
|
|
1173
|
+
*/
|
|
1174
|
+
function mapSchemaItems(node, transform) {
|
|
1175
|
+
return (node.items ?? []).map((schema) => ({
|
|
1176
|
+
schema,
|
|
1177
|
+
output: transform(schema)
|
|
1178
|
+
}));
|
|
1179
|
+
}
|
|
1180
|
+
//#endregion
|
|
1170
1181
|
//#region src/utils.ts
|
|
1171
1182
|
/**
|
|
1172
1183
|
* Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
|
|
@@ -1190,6 +1201,10 @@ function isInlineConstEnum(node, enumSchemaNames) {
|
|
|
1190
1201
|
function isSchemaOptional(schema) {
|
|
1191
1202
|
return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
|
|
1192
1203
|
}
|
|
1204
|
+
function formatExample(value) {
|
|
1205
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
1206
|
+
return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
|
|
1207
|
+
}
|
|
1193
1208
|
function buildPropertyJSDocComments(schema, optional) {
|
|
1194
1209
|
const meta = kubb_kit.ast.syncSchemaRef(schema);
|
|
1195
1210
|
const isArray = meta?.primitive === "array";
|
|
@@ -1204,7 +1219,7 @@ function buildPropertyJSDocComments(schema, optional) {
|
|
|
1204
1219
|
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
1205
1220
|
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
1206
1221
|
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,
|
|
1207
|
-
...exampleValues.map((example) => `@example ${example}`),
|
|
1222
|
+
...exampleValues.map((example) => `@example ${formatExample(example)}`),
|
|
1208
1223
|
meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
|
|
1209
1224
|
].filter(Boolean);
|
|
1210
1225
|
}
|
|
@@ -1227,7 +1242,7 @@ function buildParams({ params }) {
|
|
|
1227
1242
|
});
|
|
1228
1243
|
}
|
|
1229
1244
|
function buildOptions(node, { resolver }) {
|
|
1230
|
-
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node
|
|
1245
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
1231
1246
|
const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
1232
1247
|
const hasRequiredPath = pathParams.some((param) => param.required);
|
|
1233
1248
|
const hasRequiredQuery = queryParams.some((param) => param.required);
|
|
@@ -1437,7 +1452,8 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
|
|
|
1437
1452
|
if (!node.name) return null;
|
|
1438
1453
|
const refName = kubb_kit.ast.resolveRefName(node);
|
|
1439
1454
|
if (!refName) return null;
|
|
1440
|
-
|
|
1455
|
+
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;
|
|
1456
|
+
return createTypeReferenceNode(name, void 0);
|
|
1441
1457
|
},
|
|
1442
1458
|
enum(node) {
|
|
1443
1459
|
const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
|
|
@@ -1445,7 +1461,8 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
|
|
|
1445
1461
|
withParentheses: true,
|
|
1446
1462
|
nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
|
|
1447
1463
|
}) ?? void 0;
|
|
1448
|
-
|
|
1464
|
+
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);
|
|
1465
|
+
return createTypeReferenceNode(resolvedName, void 0);
|
|
1449
1466
|
},
|
|
1450
1467
|
union(node) {
|
|
1451
1468
|
const members = node.members ?? [];
|
|
@@ -1476,7 +1493,7 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
|
|
|
1476
1493
|
},
|
|
1477
1494
|
array(node) {
|
|
1478
1495
|
return createArrayDeclaration({
|
|
1479
|
-
nodes:
|
|
1496
|
+
nodes: mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
|
|
1480
1497
|
arrayType: this.options.arrayType
|
|
1481
1498
|
}) ?? null;
|
|
1482
1499
|
},
|
|
@@ -1486,7 +1503,7 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
|
|
|
1486
1503
|
object(node) {
|
|
1487
1504
|
const { transform, options } = this;
|
|
1488
1505
|
const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
|
|
1489
|
-
const propertyNodes =
|
|
1506
|
+
const propertyNodes = mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
|
|
1490
1507
|
const baseType = output ?? keywordTypeNodes.unknown;
|
|
1491
1508
|
const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
|
|
1492
1509
|
const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
|
|
@@ -1642,7 +1659,6 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1642
1659
|
operation(node, ctx) {
|
|
1643
1660
|
const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
|
|
1644
1661
|
const { config, resolver, root } = ctx;
|
|
1645
|
-
const params = caseParams(node.parameters, "camelcase");
|
|
1646
1662
|
const meta = { file: resolver.file({
|
|
1647
1663
|
name: node.operationId,
|
|
1648
1664
|
extname: ".ts",
|
|
@@ -1721,10 +1737,7 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1721
1737
|
name: baseName
|
|
1722
1738
|
})] });
|
|
1723
1739
|
}
|
|
1724
|
-
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(
|
|
1725
|
-
...node,
|
|
1726
|
-
parameters: params
|
|
1727
|
-
}, { paramsCasing: "original" });
|
|
1740
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
1728
1741
|
const paramGroupTypes = [
|
|
1729
1742
|
pathParams.length > 0 && renderSchemaType({
|
|
1730
1743
|
schema: buildParams({ params: pathParams }),
|
|
@@ -1771,10 +1784,7 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1771
1784
|
});
|
|
1772
1785
|
});
|
|
1773
1786
|
const optionsType = renderSchemaType({
|
|
1774
|
-
schema: buildOptions({
|
|
1775
|
-
...node,
|
|
1776
|
-
parameters: params
|
|
1777
|
-
}, { resolver }),
|
|
1787
|
+
schema: buildOptions(node, { resolver }),
|
|
1778
1788
|
name: resolver.response.options(node)
|
|
1779
1789
|
});
|
|
1780
1790
|
const responsesType = renderSchemaType({
|
|
@@ -2224,17 +2234,14 @@ function resolveGroupType({ node, params, group, resolver }) {
|
|
|
2224
2234
|
* name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
|
|
2225
2235
|
*/
|
|
2226
2236
|
function createOperationParams(node, options) {
|
|
2227
|
-
const { paramsType, pathParamsType,
|
|
2237
|
+
const { paramsType, pathParamsType, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
|
|
2228
2238
|
const dataName = paramNames?.data ?? "data";
|
|
2229
2239
|
const paramsName = paramNames?.params ?? "params";
|
|
2230
2240
|
const headersName = paramNames?.headers ?? "headers";
|
|
2231
2241
|
const pathName = paramNames?.path ?? "pathParams";
|
|
2232
2242
|
const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
|
|
2233
2243
|
const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
|
|
2234
|
-
const
|
|
2235
|
-
const pathParams = casedParams.filter((p) => p.in === "path");
|
|
2236
|
-
const queryParams = casedParams.filter((p) => p.in === "query");
|
|
2237
|
-
const headerParams = casedParams.filter((p) => p.in === "header");
|
|
2244
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
2238
2245
|
const toProperty = (param) => ({
|
|
2239
2246
|
name: param.name,
|
|
2240
2247
|
type: wrapTypeExpression(resolveParamType({
|