@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.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, { resolver }),
1765
+ schema: buildResponses(node, resolver),
1755
1766
  name: resolver.response.responses(node)
1756
1767
  });
1757
1768
  function buildResponseType() {