@kubb/plugin-ts 5.0.0 → 5.0.1
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 +166 -155
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +166 -155
- package/dist/index.js.map +1 -1
- package/package.json +17 -5
package/dist/index.js
CHANGED
|
@@ -911,6 +911,115 @@ function Enum({ node, enum: enumOptions, resolver }) {
|
|
|
911
911
|
})] });
|
|
912
912
|
}
|
|
913
913
|
//#endregion
|
|
914
|
+
//#region src/utils.ts
|
|
915
|
+
/**
|
|
916
|
+
* Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
|
|
917
|
+
* rather than a named enum reference or a runtime enum declaration.
|
|
918
|
+
*
|
|
919
|
+
* The parser folds `const` into a single-value enum node. The adapter decides which schemas are
|
|
920
|
+
* named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
|
|
921
|
+
* example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
|
|
922
|
+
* a named enum, which keeps the declaration and its references in sync across adapter versions.
|
|
923
|
+
*/
|
|
924
|
+
function isInlineConstEnum(node, enumSchemaNames) {
|
|
925
|
+
return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Collects JSDoc annotation strings for a schema node.
|
|
929
|
+
*
|
|
930
|
+
* Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
|
|
931
|
+
* Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
|
|
932
|
+
|
|
933
|
+
*/
|
|
934
|
+
function isSchemaOptional(schema) {
|
|
935
|
+
return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
|
|
936
|
+
}
|
|
937
|
+
function formatExample(value) {
|
|
938
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
939
|
+
return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
|
|
940
|
+
}
|
|
941
|
+
function buildPropertyJSDocComments(schema, optional) {
|
|
942
|
+
const meta = syncSchemaRef(schema);
|
|
943
|
+
const isArray = meta?.primitive === "array";
|
|
944
|
+
const hasDescription = meta && "description" in meta && meta.description;
|
|
945
|
+
const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
|
|
946
|
+
const exampleValues = meta?.examples ?? [];
|
|
947
|
+
const comments = [
|
|
948
|
+
hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
|
|
949
|
+
...formatComment,
|
|
950
|
+
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
|
|
951
|
+
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
|
|
952
|
+
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
953
|
+
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
954
|
+
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,
|
|
955
|
+
...exampleValues.map((example) => `@example ${formatExample(example)}`)
|
|
956
|
+
].filter(Boolean);
|
|
957
|
+
const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
|
|
958
|
+
return [...comments, typeTag].filter(Boolean);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Builds the object schema for a group of parameters sharing one `in` location (path, query, or
|
|
962
|
+
* header), embedding each param's own schema (and JSDoc) directly rather than referencing a
|
|
963
|
+
* separate per-param type — the group itself is the only type these params get exported as.
|
|
964
|
+
*/
|
|
965
|
+
function buildParams({ params }) {
|
|
966
|
+
return ast.factory.createSchema({
|
|
967
|
+
type: "object",
|
|
968
|
+
properties: params.map((param) => ast.factory.createProperty({
|
|
969
|
+
name: param.name,
|
|
970
|
+
required: param.required,
|
|
971
|
+
schema: ast.factory.createSchema({
|
|
972
|
+
...param.schema,
|
|
973
|
+
optional: !param.required
|
|
974
|
+
})
|
|
975
|
+
}))
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function buildResponseUnion(node, { resolver }) {
|
|
979
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
980
|
+
if (responsesWithSchema.length === 0) return null;
|
|
981
|
+
return ast.factory.createSchema({
|
|
982
|
+
type: "union",
|
|
983
|
+
members: responsesWithSchema.map((res) => ast.factory.createSchema({
|
|
984
|
+
type: "ref",
|
|
985
|
+
name: resolver.response.status(node, res.statusCode)
|
|
986
|
+
}))
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
//#endregion
|
|
990
|
+
//#region src/components/Type.tsx
|
|
991
|
+
function Type({ name, node, printer, enum: enumOptions, resolver }) {
|
|
992
|
+
const enumSchemaNodes = ast.collectSync(node, { schema(n) {
|
|
993
|
+
const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
|
|
994
|
+
if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
|
|
995
|
+
} });
|
|
996
|
+
const output = printer.print(node);
|
|
997
|
+
if (!output) return;
|
|
998
|
+
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
999
|
+
return {
|
|
1000
|
+
node,
|
|
1001
|
+
...getEnumNames({
|
|
1002
|
+
node,
|
|
1003
|
+
enum: enumOptions,
|
|
1004
|
+
resolver
|
|
1005
|
+
})
|
|
1006
|
+
};
|
|
1007
|
+
});
|
|
1008
|
+
const shouldExportEnums = enumOptions.type !== "inlineLiteral";
|
|
1009
|
+
const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
1010
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
|
|
1011
|
+
node,
|
|
1012
|
+
enum: enumOptions,
|
|
1013
|
+
resolver
|
|
1014
|
+
}, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
|
|
1015
|
+
name,
|
|
1016
|
+
isTypeOnly: true,
|
|
1017
|
+
isExportable: true,
|
|
1018
|
+
isIndexable: true,
|
|
1019
|
+
children: output
|
|
1020
|
+
})] });
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
914
1023
|
//#region ../../internals/shared/src/params.ts
|
|
915
1024
|
/**
|
|
916
1025
|
* Drops parameters that share the same name, keeping the first.
|
|
@@ -1054,6 +1163,62 @@ function buildOptionsSchema(node, resolver) {
|
|
|
1054
1163
|
})]
|
|
1055
1164
|
});
|
|
1056
1165
|
}
|
|
1166
|
+
/**
|
|
1167
|
+
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
1168
|
+
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
1169
|
+
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
1170
|
+
* query hooks and `result.data` use.
|
|
1171
|
+
*/
|
|
1172
|
+
function buildResponseRecordEntry(node, res, resolver) {
|
|
1173
|
+
const statusName = resolver.response.status(node, res.statusCode);
|
|
1174
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1175
|
+
if (variants.length <= 1) return ast.factory.createSchema({
|
|
1176
|
+
type: "ref",
|
|
1177
|
+
name: statusName
|
|
1178
|
+
});
|
|
1179
|
+
return ast.factory.createSchema({
|
|
1180
|
+
type: "union",
|
|
1181
|
+
members: resolveContentTypeVariants(variants, statusName).map((variant) => ast.factory.createSchema({
|
|
1182
|
+
type: "object",
|
|
1183
|
+
primitive: "object",
|
|
1184
|
+
properties: [ast.factory.createProperty({
|
|
1185
|
+
name: "contentType",
|
|
1186
|
+
required: true,
|
|
1187
|
+
schema: ast.factory.createSchema({
|
|
1188
|
+
type: "enum",
|
|
1189
|
+
enumValues: [variant.contentType]
|
|
1190
|
+
})
|
|
1191
|
+
}), ast.factory.createProperty({
|
|
1192
|
+
name: "data",
|
|
1193
|
+
required: true,
|
|
1194
|
+
schema: ast.factory.createSchema({
|
|
1195
|
+
type: "ref",
|
|
1196
|
+
name: variant.name
|
|
1197
|
+
})
|
|
1198
|
+
})]
|
|
1199
|
+
}))
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
|
|
1204
|
+
* `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
|
|
1205
|
+
* inferred responses schema, so both emit the same shape from the same inputs.
|
|
1206
|
+
*
|
|
1207
|
+
* Always emits the keyed record, even when an operation declares no responses. An operation with no
|
|
1208
|
+
* responses renders as an empty object, which keeps every consumer's import (for example the axios
|
|
1209
|
+
* SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
|
|
1210
|
+
*/
|
|
1211
|
+
function buildResponses(node, resolver) {
|
|
1212
|
+
return ast.factory.createSchema({
|
|
1213
|
+
type: "object",
|
|
1214
|
+
primitive: "object",
|
|
1215
|
+
properties: node.responses.map((res) => ast.factory.createProperty({
|
|
1216
|
+
name: String(res.statusCode),
|
|
1217
|
+
required: true,
|
|
1218
|
+
schema: buildResponseRecordEntry(node, res, resolver)
|
|
1219
|
+
}))
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1057
1222
|
//#endregion
|
|
1058
1223
|
//#region ../../internals/shared/src/resolver.ts
|
|
1059
1224
|
/**
|
|
@@ -1204,160 +1369,6 @@ function mapSchemaItems(node, transform) {
|
|
|
1204
1369
|
}));
|
|
1205
1370
|
}
|
|
1206
1371
|
//#endregion
|
|
1207
|
-
//#region src/utils.ts
|
|
1208
|
-
/**
|
|
1209
|
-
* Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
|
|
1210
|
-
* rather than a named enum reference or a runtime enum declaration.
|
|
1211
|
-
*
|
|
1212
|
-
* The parser folds `const` into a single-value enum node. The adapter decides which schemas are
|
|
1213
|
-
* named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
|
|
1214
|
-
* example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
|
|
1215
|
-
* a named enum, which keeps the declaration and its references in sync across adapter versions.
|
|
1216
|
-
*/
|
|
1217
|
-
function isInlineConstEnum(node, enumSchemaNames) {
|
|
1218
|
-
return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
|
|
1219
|
-
}
|
|
1220
|
-
/**
|
|
1221
|
-
* Collects JSDoc annotation strings for a schema node.
|
|
1222
|
-
*
|
|
1223
|
-
* Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
|
|
1224
|
-
* Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
|
|
1225
|
-
|
|
1226
|
-
*/
|
|
1227
|
-
function isSchemaOptional(schema) {
|
|
1228
|
-
return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
|
|
1229
|
-
}
|
|
1230
|
-
function formatExample(value) {
|
|
1231
|
-
if (value === null || typeof value !== "object") return String(value);
|
|
1232
|
-
return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
|
|
1233
|
-
}
|
|
1234
|
-
function buildPropertyJSDocComments(schema, optional) {
|
|
1235
|
-
const meta = syncSchemaRef(schema);
|
|
1236
|
-
const isArray = meta?.primitive === "array";
|
|
1237
|
-
const hasDescription = meta && "description" in meta && meta.description;
|
|
1238
|
-
const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
|
|
1239
|
-
const exampleValues = meta?.examples ?? [];
|
|
1240
|
-
const comments = [
|
|
1241
|
-
hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
|
|
1242
|
-
...formatComment,
|
|
1243
|
-
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
|
|
1244
|
-
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
|
|
1245
|
-
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
1246
|
-
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
1247
|
-
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,
|
|
1248
|
-
...exampleValues.map((example) => `@example ${formatExample(example)}`)
|
|
1249
|
-
].filter(Boolean);
|
|
1250
|
-
const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
|
|
1251
|
-
return [...comments, typeTag].filter(Boolean);
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Builds the object schema for a group of parameters sharing one `in` location (path, query, or
|
|
1255
|
-
* header), embedding each param's own schema (and JSDoc) directly rather than referencing a
|
|
1256
|
-
* separate per-param type — the group itself is the only type these params get exported as.
|
|
1257
|
-
*/
|
|
1258
|
-
function buildParams({ params }) {
|
|
1259
|
-
return ast.factory.createSchema({
|
|
1260
|
-
type: "object",
|
|
1261
|
-
properties: params.map((param) => ast.factory.createProperty({
|
|
1262
|
-
name: param.name,
|
|
1263
|
-
required: param.required,
|
|
1264
|
-
schema: ast.factory.createSchema({
|
|
1265
|
-
...param.schema,
|
|
1266
|
-
optional: !param.required
|
|
1267
|
-
})
|
|
1268
|
-
}))
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
1273
|
-
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
1274
|
-
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
1275
|
-
* query hooks and `result.data` use.
|
|
1276
|
-
*/
|
|
1277
|
-
function buildResponseRecordEntry(node, res, resolver) {
|
|
1278
|
-
const statusName = resolver.response.status(node, res.statusCode);
|
|
1279
|
-
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1280
|
-
if (variants.length <= 1) return ast.factory.createSchema({
|
|
1281
|
-
type: "ref",
|
|
1282
|
-
name: statusName
|
|
1283
|
-
});
|
|
1284
|
-
return ast.factory.createSchema({
|
|
1285
|
-
type: "union",
|
|
1286
|
-
members: resolveContentTypeVariants(variants, statusName).map((variant) => ast.factory.createSchema({
|
|
1287
|
-
type: "object",
|
|
1288
|
-
properties: [ast.factory.createProperty({
|
|
1289
|
-
name: "contentType",
|
|
1290
|
-
required: true,
|
|
1291
|
-
schema: ast.factory.createSchema({
|
|
1292
|
-
type: "enum",
|
|
1293
|
-
enumValues: [variant.contentType]
|
|
1294
|
-
})
|
|
1295
|
-
}), ast.factory.createProperty({
|
|
1296
|
-
name: "data",
|
|
1297
|
-
required: true,
|
|
1298
|
-
schema: ast.factory.createSchema({
|
|
1299
|
-
type: "ref",
|
|
1300
|
-
name: variant.name
|
|
1301
|
-
})
|
|
1302
|
-
})]
|
|
1303
|
-
}))
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
function buildResponses(node, { resolver }) {
|
|
1307
|
-
return ast.factory.createSchema({
|
|
1308
|
-
type: "object",
|
|
1309
|
-
properties: node.responses.map((res) => ast.factory.createProperty({
|
|
1310
|
-
name: String(res.statusCode),
|
|
1311
|
-
required: true,
|
|
1312
|
-
schema: buildResponseRecordEntry(node, res, resolver)
|
|
1313
|
-
}))
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
function buildResponseUnion(node, { resolver }) {
|
|
1317
|
-
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
1318
|
-
if (responsesWithSchema.length === 0) return null;
|
|
1319
|
-
return ast.factory.createSchema({
|
|
1320
|
-
type: "union",
|
|
1321
|
-
members: responsesWithSchema.map((res) => ast.factory.createSchema({
|
|
1322
|
-
type: "ref",
|
|
1323
|
-
name: resolver.response.status(node, res.statusCode)
|
|
1324
|
-
}))
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
//#endregion
|
|
1328
|
-
//#region src/components/Type.tsx
|
|
1329
|
-
function Type({ name, node, printer, enum: enumOptions, resolver }) {
|
|
1330
|
-
const enumSchemaNodes = ast.collectSync(node, { schema(n) {
|
|
1331
|
-
const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
|
|
1332
|
-
if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
|
|
1333
|
-
} });
|
|
1334
|
-
const output = printer.print(node);
|
|
1335
|
-
if (!output) return;
|
|
1336
|
-
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
1337
|
-
return {
|
|
1338
|
-
node,
|
|
1339
|
-
...getEnumNames({
|
|
1340
|
-
node,
|
|
1341
|
-
enum: enumOptions,
|
|
1342
|
-
resolver
|
|
1343
|
-
})
|
|
1344
|
-
};
|
|
1345
|
-
});
|
|
1346
|
-
const shouldExportEnums = enumOptions.type !== "inlineLiteral";
|
|
1347
|
-
const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
1348
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
|
|
1349
|
-
node,
|
|
1350
|
-
enum: enumOptions,
|
|
1351
|
-
resolver
|
|
1352
|
-
}, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
|
|
1353
|
-
name,
|
|
1354
|
-
isTypeOnly: true,
|
|
1355
|
-
isExportable: true,
|
|
1356
|
-
isIndexable: true,
|
|
1357
|
-
children: output
|
|
1358
|
-
})] });
|
|
1359
|
-
}
|
|
1360
|
-
//#endregion
|
|
1361
1372
|
//#region src/printers/printerTs.ts
|
|
1362
1373
|
const { isNonNullable } = factory_exports;
|
|
1363
1374
|
/**
|
|
@@ -1751,7 +1762,7 @@ const typeGenerator = defineGenerator({
|
|
|
1751
1762
|
name: resolver.response.options(node)
|
|
1752
1763
|
});
|
|
1753
1764
|
const responsesType = renderSchemaType({
|
|
1754
|
-
schema: buildResponses(node,
|
|
1765
|
+
schema: buildResponses(node, resolver),
|
|
1755
1766
|
name: resolver.response.responses(node)
|
|
1756
1767
|
});
|
|
1757
1768
|
function buildResponseType() {
|