@kubb/plugin-ts 5.0.0 → 5.0.2

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
@@ -665,8 +665,10 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
665
665
  }).filter((node) => node !== void 0)))];
666
666
  if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value, description]) => {
667
667
  let initializer = factory.createStringLiteral(value?.toString());
668
- if (Number.parseInt(value.toString(), 10) === value && isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
669
- else initializer = factory.createNumericLiteral(value);
668
+ if (Number.parseInt(value.toString(), 10) === value && isNumber(value)) {
669
+ if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
670
+ else initializer = factory.createNumericLiteral(value);
671
+ }
670
672
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
671
673
  if (isNumber(Number.parseInt(key.toString(), 10))) {
672
674
  const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
@@ -687,8 +689,10 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
687
689
  if (enums.length === 0) return [void 0, factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createKeywordTypeNode(typescript.default.SyntaxKind.NeverKeyword))];
688
690
  return [factory.createVariableStatement([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value, description]) => {
689
691
  let initializer = factory.createStringLiteral(value?.toString());
690
- if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
691
- else initializer = factory.createNumericLiteral(value);
692
+ if (isNumber(value)) {
693
+ if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
694
+ else initializer = factory.createNumericLiteral(value);
695
+ }
692
696
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
693
697
  if (key !== null && key !== void 0) {
694
698
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
@@ -950,6 +954,115 @@ function Enum({ node, enum: enumOptions, resolver }) {
950
954
  })] });
951
955
  }
952
956
  //#endregion
957
+ //#region src/utils.ts
958
+ /**
959
+ * Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
960
+ * rather than a named enum reference or a runtime enum declaration.
961
+ *
962
+ * The parser folds `const` into a single-value enum node. The adapter decides which schemas are
963
+ * named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
964
+ * example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
965
+ * a named enum, which keeps the declaration and its references in sync across adapter versions.
966
+ */
967
+ function isInlineConstEnum(node, enumSchemaNames) {
968
+ return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
969
+ }
970
+ /**
971
+ * Collects JSDoc annotation strings for a schema node.
972
+ *
973
+ * Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
974
+ * Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
975
+
976
+ */
977
+ function isSchemaOptional(schema) {
978
+ return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
979
+ }
980
+ function formatExample(value) {
981
+ if (value === null || typeof value !== "object") return String(value);
982
+ return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
983
+ }
984
+ function buildPropertyJSDocComments(schema, optional) {
985
+ const meta = (0, kubb_kit.syncSchemaRef)(schema);
986
+ const isArray = meta?.primitive === "array";
987
+ const hasDescription = meta && "description" in meta && meta.description;
988
+ const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
989
+ const exampleValues = meta?.examples ?? [];
990
+ const comments = [
991
+ hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
992
+ ...formatComment,
993
+ meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
994
+ !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
995
+ !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
996
+ meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
997
+ 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,
998
+ ...exampleValues.map((example) => `@example ${formatExample(example)}`)
999
+ ].filter(Boolean);
1000
+ const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
1001
+ return [...comments, typeTag].filter(Boolean);
1002
+ }
1003
+ /**
1004
+ * Builds the object schema for a group of parameters sharing one `in` location (path, query, or
1005
+ * header), embedding each param's own schema (and JSDoc) directly rather than referencing a
1006
+ * separate per-param type — the group itself is the only type these params get exported as.
1007
+ */
1008
+ function buildParams({ params }) {
1009
+ return kubb_kit.ast.factory.createSchema({
1010
+ type: "object",
1011
+ properties: params.map((param) => kubb_kit.ast.factory.createProperty({
1012
+ name: param.name,
1013
+ required: param.required,
1014
+ schema: kubb_kit.ast.factory.createSchema({
1015
+ ...param.schema,
1016
+ optional: !param.required
1017
+ })
1018
+ }))
1019
+ });
1020
+ }
1021
+ function buildResponseUnion(node, { resolver }) {
1022
+ const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
1023
+ if (responsesWithSchema.length === 0) return null;
1024
+ return kubb_kit.ast.factory.createSchema({
1025
+ type: "union",
1026
+ members: responsesWithSchema.map((res) => kubb_kit.ast.factory.createSchema({
1027
+ type: "ref",
1028
+ name: resolver.response.status(node, res.statusCode)
1029
+ }))
1030
+ });
1031
+ }
1032
+ //#endregion
1033
+ //#region src/components/Type.tsx
1034
+ function Type({ name, node, printer, enum: enumOptions, resolver }) {
1035
+ const enumSchemaNodes = kubb_kit.ast.collectSync(node, { schema(n) {
1036
+ const enumNode = kubb_kit.ast.narrowSchema(n, kubb_kit.ast.schemaTypes.enum);
1037
+ if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
1038
+ } });
1039
+ const output = printer.print(node);
1040
+ if (!output) return;
1041
+ const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
1042
+ return {
1043
+ node,
1044
+ ...getEnumNames({
1045
+ node,
1046
+ enum: enumOptions,
1047
+ resolver
1048
+ })
1049
+ };
1050
+ });
1051
+ const shouldExportEnums = enumOptions.type !== "inlineLiteral";
1052
+ const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
1053
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Enum, {
1054
+ node,
1055
+ enum: enumOptions,
1056
+ resolver
1057
+ }, node.name)), shouldExportType && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
1058
+ name,
1059
+ isTypeOnly: true,
1060
+ isExportable: true,
1061
+ isIndexable: true,
1062
+ children: output
1063
+ })] });
1064
+ }
1065
+ //#endregion
953
1066
  //#region ../../internals/shared/src/params.ts
954
1067
  /**
955
1068
  * Drops parameters that share the same name, keeping the first.
@@ -1093,6 +1206,62 @@ function buildOptionsSchema(node, resolver) {
1093
1206
  })]
1094
1207
  });
1095
1208
  }
1209
+ /**
1210
+ * The schema a status occupies in the `<Name>Responses` record. A status that documents several
1211
+ * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
1212
+ * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
1213
+ * query hooks and `result.data` use.
1214
+ */
1215
+ function buildResponseRecordEntry(node, res, resolver) {
1216
+ const statusName = resolver.response.status(node, res.statusCode);
1217
+ const variants = (res.content ?? []).filter((entry) => entry.schema);
1218
+ if (variants.length <= 1) return kubb_kit.ast.factory.createSchema({
1219
+ type: "ref",
1220
+ name: statusName
1221
+ });
1222
+ return kubb_kit.ast.factory.createSchema({
1223
+ type: "union",
1224
+ members: resolveContentTypeVariants(variants, statusName).map((variant) => kubb_kit.ast.factory.createSchema({
1225
+ type: "object",
1226
+ primitive: "object",
1227
+ properties: [kubb_kit.ast.factory.createProperty({
1228
+ name: "contentType",
1229
+ required: true,
1230
+ schema: kubb_kit.ast.factory.createSchema({
1231
+ type: "enum",
1232
+ enumValues: [variant.contentType]
1233
+ })
1234
+ }), kubb_kit.ast.factory.createProperty({
1235
+ name: "data",
1236
+ required: true,
1237
+ schema: kubb_kit.ast.factory.createSchema({
1238
+ type: "ref",
1239
+ name: variant.name
1240
+ })
1241
+ })]
1242
+ }))
1243
+ });
1244
+ }
1245
+ /**
1246
+ * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
1247
+ * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
1248
+ * inferred responses schema, so both emit the same shape from the same inputs.
1249
+ *
1250
+ * Always emits the keyed record, even when an operation declares no responses. An operation with no
1251
+ * responses renders as an empty object, which keeps every consumer's import (for example the axios
1252
+ * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
1253
+ */
1254
+ function buildResponses(node, resolver) {
1255
+ return kubb_kit.ast.factory.createSchema({
1256
+ type: "object",
1257
+ primitive: "object",
1258
+ properties: node.responses.map((res) => kubb_kit.ast.factory.createProperty({
1259
+ name: String(res.statusCode),
1260
+ required: true,
1261
+ schema: buildResponseRecordEntry(node, res, resolver)
1262
+ }))
1263
+ });
1264
+ }
1096
1265
  //#endregion
1097
1266
  //#region ../../internals/shared/src/resolver.ts
1098
1267
  /**
@@ -1243,160 +1412,6 @@ function mapSchemaItems(node, transform) {
1243
1412
  }));
1244
1413
  }
1245
1414
  //#endregion
1246
- //#region src/utils.ts
1247
- /**
1248
- * Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
1249
- * rather than a named enum reference or a runtime enum declaration.
1250
- *
1251
- * The parser folds `const` into a single-value enum node. The adapter decides which schemas are
1252
- * named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
1253
- * example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
1254
- * a named enum, which keeps the declaration and its references in sync across adapter versions.
1255
- */
1256
- function isInlineConstEnum(node, enumSchemaNames) {
1257
- return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
1258
- }
1259
- /**
1260
- * Collects JSDoc annotation strings for a schema node.
1261
- *
1262
- * Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
1263
- * Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
1264
-
1265
- */
1266
- function isSchemaOptional(schema) {
1267
- return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
1268
- }
1269
- function formatExample(value) {
1270
- if (value === null || typeof value !== "object") return String(value);
1271
- return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
1272
- }
1273
- function buildPropertyJSDocComments(schema, optional) {
1274
- const meta = (0, kubb_kit.syncSchemaRef)(schema);
1275
- const isArray = meta?.primitive === "array";
1276
- const hasDescription = meta && "description" in meta && meta.description;
1277
- const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
1278
- const exampleValues = meta?.examples ?? [];
1279
- const comments = [
1280
- hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
1281
- ...formatComment,
1282
- meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
1283
- !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
1284
- !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
1285
- meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
1286
- 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,
1287
- ...exampleValues.map((example) => `@example ${formatExample(example)}`)
1288
- ].filter(Boolean);
1289
- const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
1290
- return [...comments, typeTag].filter(Boolean);
1291
- }
1292
- /**
1293
- * Builds the object schema for a group of parameters sharing one `in` location (path, query, or
1294
- * header), embedding each param's own schema (and JSDoc) directly rather than referencing a
1295
- * separate per-param type — the group itself is the only type these params get exported as.
1296
- */
1297
- function buildParams({ params }) {
1298
- return kubb_kit.ast.factory.createSchema({
1299
- type: "object",
1300
- properties: params.map((param) => kubb_kit.ast.factory.createProperty({
1301
- name: param.name,
1302
- required: param.required,
1303
- schema: kubb_kit.ast.factory.createSchema({
1304
- ...param.schema,
1305
- optional: !param.required
1306
- })
1307
- }))
1308
- });
1309
- }
1310
- /**
1311
- * The schema a status occupies in the `<Name>Responses` record. A status that documents several
1312
- * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
1313
- * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
1314
- * query hooks and `result.data` use.
1315
- */
1316
- function buildResponseRecordEntry(node, res, resolver) {
1317
- const statusName = resolver.response.status(node, res.statusCode);
1318
- const variants = (res.content ?? []).filter((entry) => entry.schema);
1319
- if (variants.length <= 1) return kubb_kit.ast.factory.createSchema({
1320
- type: "ref",
1321
- name: statusName
1322
- });
1323
- return kubb_kit.ast.factory.createSchema({
1324
- type: "union",
1325
- members: resolveContentTypeVariants(variants, statusName).map((variant) => kubb_kit.ast.factory.createSchema({
1326
- type: "object",
1327
- properties: [kubb_kit.ast.factory.createProperty({
1328
- name: "contentType",
1329
- required: true,
1330
- schema: kubb_kit.ast.factory.createSchema({
1331
- type: "enum",
1332
- enumValues: [variant.contentType]
1333
- })
1334
- }), kubb_kit.ast.factory.createProperty({
1335
- name: "data",
1336
- required: true,
1337
- schema: kubb_kit.ast.factory.createSchema({
1338
- type: "ref",
1339
- name: variant.name
1340
- })
1341
- })]
1342
- }))
1343
- });
1344
- }
1345
- function buildResponses(node, { resolver }) {
1346
- return kubb_kit.ast.factory.createSchema({
1347
- type: "object",
1348
- properties: node.responses.map((res) => kubb_kit.ast.factory.createProperty({
1349
- name: String(res.statusCode),
1350
- required: true,
1351
- schema: buildResponseRecordEntry(node, res, resolver)
1352
- }))
1353
- });
1354
- }
1355
- function buildResponseUnion(node, { resolver }) {
1356
- const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
1357
- if (responsesWithSchema.length === 0) return null;
1358
- return kubb_kit.ast.factory.createSchema({
1359
- type: "union",
1360
- members: responsesWithSchema.map((res) => kubb_kit.ast.factory.createSchema({
1361
- type: "ref",
1362
- name: resolver.response.status(node, res.statusCode)
1363
- }))
1364
- });
1365
- }
1366
- //#endregion
1367
- //#region src/components/Type.tsx
1368
- function Type({ name, node, printer, enum: enumOptions, resolver }) {
1369
- const enumSchemaNodes = kubb_kit.ast.collectSync(node, { schema(n) {
1370
- const enumNode = kubb_kit.ast.narrowSchema(n, kubb_kit.ast.schemaTypes.enum);
1371
- if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
1372
- } });
1373
- const output = printer.print(node);
1374
- if (!output) return;
1375
- const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
1376
- return {
1377
- node,
1378
- ...getEnumNames({
1379
- node,
1380
- enum: enumOptions,
1381
- resolver
1382
- })
1383
- };
1384
- });
1385
- const shouldExportEnums = enumOptions.type !== "inlineLiteral";
1386
- const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
1387
- return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Enum, {
1388
- node,
1389
- enum: enumOptions,
1390
- resolver
1391
- }, node.name)), shouldExportType && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
1392
- name,
1393
- isTypeOnly: true,
1394
- isExportable: true,
1395
- isIndexable: true,
1396
- children: output
1397
- })] });
1398
- }
1399
- //#endregion
1400
1415
  //#region src/printers/printerTs.ts
1401
1416
  const { isNonNullable } = factory_exports;
1402
1417
  /**
@@ -1458,7 +1473,7 @@ const printerTs = kubb_kit.ast.createPrinter((options) => {
1458
1473
  return createTypeReferenceNode(name, void 0);
1459
1474
  },
1460
1475
  enum(node) {
1461
- const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
1476
+ const values = kubb_kit.ast.getSchemaLiteralValues(node);
1462
1477
  if (this.options.enum.type === "inlineLiteral" || !node.name || isInlineConstEnum(node, this.options.enumSchemaNames)) return createUnionDeclaration({
1463
1478
  withParentheses: true,
1464
1479
  nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
@@ -1790,7 +1805,7 @@ const typeGenerator = (0, kubb_kit.defineGenerator)({
1790
1805
  name: resolver.response.options(node)
1791
1806
  });
1792
1807
  const responsesType = renderSchemaType({
1793
- schema: buildResponses(node, { resolver }),
1808
+ schema: buildResponses(node, resolver),
1794
1809
  name: resolver.response.responses(node)
1795
1810
  });
1796
1811
  function buildResponseType() {