@kubb/plugin-ts 5.0.0-beta.75 → 5.0.0-beta.77

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
@@ -550,7 +550,7 @@ function applyEnumKeyCasing(key, casing = "none") {
550
550
  * type: 'enum',
551
551
  * name: 'petType',
552
552
  * typeName: 'PetType',
553
- * enums: [['cat', 'cat'], ['dog', 'dog']],
553
+ * enums: [['cat', 'cat', 'A cat'], ['dog', 'dog']],
554
554
  * })
555
555
  * ```
556
556
  */
@@ -563,30 +563,39 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
563
563
  if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
564
564
  if (value !== null && value !== void 0) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
565
565
  }).filter((node) => node !== void 0)))];
566
- if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(ts.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
566
+ if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(ts.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value, description]) => {
567
567
  let initializer = factory.createStringLiteral(value?.toString());
568
568
  if (Number.parseInt(value.toString(), 10) === value && isNumber(Number.parseInt(value.toString(), 10))) if (value < 0) initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
569
569
  else initializer = factory.createNumericLiteral(value);
570
570
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
571
571
  if (isNumber(Number.parseInt(key.toString(), 10))) {
572
572
  const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
573
- return factory.createEnumMember(propertyName(casingKey), initializer);
573
+ return appendJSDocToNode({
574
+ node: factory.createEnumMember(propertyName(casingKey), initializer),
575
+ comments: [description]
576
+ });
574
577
  }
575
578
  if (key !== null && key !== void 0) {
576
579
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
577
- return factory.createEnumMember(propertyName(casingKey), initializer);
580
+ return appendJSDocToNode({
581
+ node: factory.createEnumMember(propertyName(casingKey), initializer),
582
+ comments: [description]
583
+ });
578
584
  }
579
585
  }).filter((member) => member !== void 0))];
580
586
  const identifierName = name;
581
587
  if (enums.length === 0) return [void 0, factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))];
582
- return [factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value]) => {
588
+ return [factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value, description]) => {
583
589
  let initializer = factory.createStringLiteral(value?.toString());
584
590
  if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
585
591
  else initializer = factory.createNumericLiteral(value);
586
592
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
587
593
  if (key !== null && key !== void 0) {
588
594
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
589
- return factory.createPropertyAssignment(propertyName(casingKey), initializer);
595
+ return appendJSDocToNode({
596
+ node: factory.createPropertyAssignment(propertyName(casingKey), initializer),
597
+ comments: [description]
598
+ });
590
599
  }
591
600
  }).filter((property) => property !== void 0), true), factory.createTypeReferenceNode(factory.createIdentifier("const"), void 0)))], ts.NodeFlags.Const)), factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0)), factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0))))];
592
601
  }
@@ -753,24 +762,30 @@ function buildPropertyType(schema, baseType, optionalType, optional) {
753
762
  if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
754
763
  return type;
755
764
  }
765
+ const indexSignaturePrinter = ts.createPrinter();
766
+ const indexSignatureSource = ts.createSourceFile("", "", ts.ScriptTarget.Latest);
756
767
  /**
757
- * Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
768
+ * Creates a TypeScript index signature for `additionalProperties` and `patternProperties` on an
769
+ * object schema node. TypeScript allows only one string index signature and its value must be
770
+ * assignable from every named property, so both keywords collapse into a single signature: the
771
+ * union of all value types (deduplicated), or `unknown` when the object also has fixed properties.
772
+ * The key regex of `patternProperties` cannot be expressed by an index signature and is dropped.
758
773
  */
759
774
  function buildIndexSignatures(node, propertyCount, print) {
760
- const elements = [];
761
- if (node.additionalProperties && node.additionalProperties !== true) {
762
- const additionalType = print(node.additionalProperties) ?? keywordTypeNodes.unknown;
763
- elements.push(createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : additionalType));
764
- } else if (node.additionalProperties === true) elements.push(createIndexSignature(keywordTypeNodes.unknown));
765
- if (node.patternProperties) {
766
- const first = Object.values(node.patternProperties)[0];
767
- if (first) {
768
- let patternType = print(first) ?? keywordTypeNodes.unknown;
769
- if (first.nullable) patternType = createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] });
770
- elements.push(createIndexSignature(patternType));
771
- }
775
+ const valueTypes = [];
776
+ if (node.additionalProperties && node.additionalProperties !== true) valueTypes.push(print(node.additionalProperties) ?? keywordTypeNodes.unknown);
777
+ else if (node.additionalProperties === true) valueTypes.push(keywordTypeNodes.unknown);
778
+ for (const schema of Object.values(node.patternProperties ?? {})) {
779
+ const patternType = print(schema) ?? keywordTypeNodes.unknown;
780
+ valueTypes.push(schema.nullable ? createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] }) : patternType);
772
781
  }
773
- return elements;
782
+ if (valueTypes.length === 0) return [];
783
+ const seen = /* @__PURE__ */ new Set();
784
+ const distinct = valueTypes.filter((type) => {
785
+ const key = indexSignaturePrinter.printNode(ts.EmitHint.Unspecified, type, indexSignatureSource);
786
+ return seen.has(key) ? false : (seen.add(key), true);
787
+ });
788
+ return [createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : createUnionDeclaration({ nodes: distinct }))];
774
789
  }
775
790
  //#endregion
776
791
  //#region src/components/Enum.tsx
@@ -811,7 +826,11 @@ function Enum({ node, enum: enumOptions, resolver }) {
811
826
  const [nameNode, typeNode] = createEnumDeclaration({
812
827
  name: enumName,
813
828
  typeName,
814
- enums: node.namedEnumValues?.map((v) => [trimQuotes(v.name.toString()), v.value]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
829
+ enums: node.namedEnumValues?.map((v) => [
830
+ trimQuotes(v.name.toString()),
831
+ v.value,
832
+ v.description
833
+ ]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
815
834
  type: enumOptions.type,
816
835
  enumKeyCasing: enumOptions.keyCasing
817
836
  });
@@ -831,39 +850,6 @@ function Enum({ node, enum: enumOptions, resolver }) {
831
850
  })] });
832
851
  }
833
852
  //#endregion
834
- //#region src/components/Type.tsx
835
- function Type({ name, node, printer, enum: enumOptions, resolver }) {
836
- const enumSchemaNodes = ast.collect(node, { schema(n) {
837
- const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
838
- if (enumNode?.name) return enumNode;
839
- } });
840
- const output = printer.print(node);
841
- if (!output) return;
842
- const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
843
- return {
844
- node,
845
- ...getEnumNames({
846
- node,
847
- enum: enumOptions,
848
- resolver
849
- })
850
- };
851
- });
852
- const shouldExportEnums = enumOptions.type !== "inlineLiteral";
853
- const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
854
- return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
855
- node,
856
- enum: enumOptions,
857
- resolver
858
- }, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
859
- name,
860
- isTypeOnly: true,
861
- isExportable: true,
862
- isIndexable: true,
863
- children: output
864
- })] });
865
- }
866
- //#endregion
867
853
  //#region ../../internals/shared/src/params.ts
868
854
  const caseParamsCache = /* @__PURE__ */ new WeakMap();
869
855
  /**
@@ -977,6 +963,18 @@ function createGroupConfig(group) {
977
963
  //#endregion
978
964
  //#region src/utils.ts
979
965
  /**
966
+ * Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
967
+ * rather than a named enum reference or a runtime enum declaration.
968
+ *
969
+ * The parser folds `const` into a single-value enum node. The adapter decides which schemas are
970
+ * named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
971
+ * example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
972
+ * a named enum, which keeps the declaration and its references in sync across adapter versions.
973
+ */
974
+ function isInlineConstEnum(node, enumSchemaNames) {
975
+ return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
976
+ }
977
+ /**
980
978
  * Collects JSDoc annotation strings for a schema node.
981
979
  *
982
980
  * Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
@@ -1114,6 +1112,39 @@ function buildResponseUnion(node, { resolver }) {
1114
1112
  });
1115
1113
  }
1116
1114
  //#endregion
1115
+ //#region src/components/Type.tsx
1116
+ function Type({ name, node, printer, enum: enumOptions, resolver }) {
1117
+ const enumSchemaNodes = ast.collect(node, { schema(n) {
1118
+ const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
1119
+ if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
1120
+ } });
1121
+ const output = printer.print(node);
1122
+ if (!output) return;
1123
+ const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
1124
+ return {
1125
+ node,
1126
+ ...getEnumNames({
1127
+ node,
1128
+ enum: enumOptions,
1129
+ resolver
1130
+ })
1131
+ };
1132
+ });
1133
+ const shouldExportEnums = enumOptions.type !== "inlineLiteral";
1134
+ const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
1135
+ return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
1136
+ node,
1137
+ enum: enumOptions,
1138
+ resolver
1139
+ }, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
1140
+ name,
1141
+ isTypeOnly: true,
1142
+ isExportable: true,
1143
+ isIndexable: true,
1144
+ children: output
1145
+ })] });
1146
+ }
1147
+ //#endregion
1117
1148
  //#region src/printers/printerTs.ts
1118
1149
  const { isNonNullable } = factory_exports;
1119
1150
  /**
@@ -1174,7 +1205,7 @@ const printerTs = ast.createPrinter((options) => {
1174
1205
  },
1175
1206
  enum(node) {
1176
1207
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
1177
- if (this.options.enum.type === "inlineLiteral" || !node.name) return createUnionDeclaration({
1208
+ if (this.options.enum.type === "inlineLiteral" || !node.name || isInlineConstEnum(node, this.options.enumSchemaNames)) return createUnionDeclaration({
1178
1209
  withParentheses: true,
1179
1210
  nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
1180
1211
  }) ?? void 0;
@@ -1304,7 +1335,8 @@ const typeGenerator = defineGenerator({
1304
1335
  group: group ?? void 0
1305
1336
  }).path
1306
1337
  }));
1307
- const isEnumSchema = !!ast.narrowSchema(node, ast.schemaTypes.enum);
1338
+ const enumNode = ast.narrowSchema(node, ast.schemaTypes.enum);
1339
+ const isEnumSchema = !!enumNode && !isInlineConstEnum(enumNode, enumSchemaNames);
1308
1340
  const meta = {
1309
1341
  name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolver.resolveTypeName(node.name),
1310
1342
  file: resolver.resolveFile({