@kubb/plugin-ts 5.0.0-alpha.23 → 5.0.0-alpha.24

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.d.ts CHANGED
@@ -90,6 +90,87 @@ type ResolverTs = Resolver & OperationParamsResolver & {
90
90
  */
91
91
  resolveHeaderParamsName(node: OperationNode, param: ParameterNode): string;
92
92
  };
93
+ type EnumKeyCasing = 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none';
94
+ /**
95
+ * Discriminated union that ties `enumTypeSuffix` and `enumKeyCasing` to the enum types that actually use them.
96
+ *
97
+ * - `'asConst'` / `'asPascalConst'` — emit a `const` object; both `enumTypeSuffix` (type-alias suffix) and
98
+ * `enumKeyCasing` (key formatting) are meaningful.
99
+ * - `'enum'` / `'constEnum'` — emit a TypeScript enum; `enumKeyCasing` applies to member names,
100
+ * but there is no separate type alias so `enumTypeSuffix` is not used.
101
+ * - `'literal'` / `'inlineLiteral'` — emit only union literals; keys are discarded entirely,
102
+ * so neither `enumTypeSuffix` nor `enumKeyCasing` have any effect.
103
+ */
104
+ type EnumTypeOptions = {
105
+ /**
106
+ * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
107
+ * - 'asConst' generates const objects with camelCase names and as const assertion.
108
+ * - 'asPascalConst' generates const objects with PascalCase names and as const assertion.
109
+ * @default 'asConst'
110
+ */
111
+ enumType?: 'asConst' | 'asPascalConst';
112
+ /**
113
+ * Suffix appended to the generated type alias name.
114
+ *
115
+ * Only affects the type alias — the const object name is unchanged.
116
+ *
117
+ * @default 'Key'
118
+ * @example enumTypeSuffix: 'Value' → `export type PetStatusValue = …`
119
+ */
120
+ enumTypeSuffix?: string;
121
+ /**
122
+ * Choose the casing for enum key names.
123
+ * - 'screamingSnakeCase' generates keys in SCREAMING_SNAKE_CASE format.
124
+ * - 'snakeCase' generates keys in snake_case format.
125
+ * - 'pascalCase' generates keys in PascalCase format.
126
+ * - 'camelCase' generates keys in camelCase format.
127
+ * - 'none' uses the enum value as-is without transformation.
128
+ * @default 'none'
129
+ */
130
+ enumKeyCasing?: EnumKeyCasing;
131
+ } | {
132
+ /**
133
+ * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
134
+ * - 'enum' generates TypeScript enum declarations.
135
+ * - 'constEnum' generates TypeScript const enum declarations.
136
+ * @default 'asConst'
137
+ */
138
+ enumType?: 'enum' | 'constEnum';
139
+ /**
140
+ * `enumTypeSuffix` has no effect for this `enumType`.
141
+ * It is only used when `enumType` is `'asConst'` or `'asPascalConst'`.
142
+ */
143
+ enumTypeSuffix?: never;
144
+ /**
145
+ * Choose the casing for enum key names.
146
+ * - 'screamingSnakeCase' generates keys in SCREAMING_SNAKE_CASE format.
147
+ * - 'snakeCase' generates keys in snake_case format.
148
+ * - 'pascalCase' generates keys in PascalCase format.
149
+ * - 'camelCase' generates keys in camelCase format.
150
+ * - 'none' uses the enum value as-is without transformation.
151
+ * @default 'none'
152
+ */
153
+ enumKeyCasing?: EnumKeyCasing;
154
+ } | {
155
+ /**
156
+ * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
157
+ * - 'literal' generates literal union types.
158
+ * - 'inlineLiteral' inlines enum values directly into the type (default in v5).
159
+ * @default 'asConst'
160
+ * @note In Kubb v5, 'inlineLiteral' becomes the default.
161
+ */
162
+ enumType?: 'literal' | 'inlineLiteral';
163
+ /**
164
+ * `enumTypeSuffix` has no effect for this `enumType`.
165
+ * It is only used when `enumType` is `'asConst'` or `'asPascalConst'`.
166
+ */
167
+ enumTypeSuffix?: never;
168
+ /**
169
+ * `enumKeyCasing` has no effect for this `enumType`.
170
+ * Literal and inlineLiteral modes emit only values — keys are discarded entirely.
171
+ */
172
+ enumKeyCasing?: never;
173
+ };
93
174
  type Options = {
94
175
  /**
95
176
  * Specify the export location for the files and define the behavior of the output
@@ -117,37 +198,6 @@ type Options = {
117
198
  * Array containing override parameters to override `options` based on tags/operations/methods/paths.
118
199
  */
119
200
  override?: Array<Override<ResolvedOptions>>;
120
- /**
121
- * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
122
- * - 'enum' generates TypeScript enum declarations.
123
- * - 'asConst' generates const objects with camelCase names and as const assertion.
124
- * - 'asPascalConst' generates const objects with PascalCase names and as const assertion.
125
- * - 'constEnum' generates TypeScript const enum declarations.
126
- * - 'literal' generates literal union types.
127
- * - 'inlineLiteral' inline enum values directly into the type (default in v5).
128
- * @default 'asConst'
129
- * @note In Kubb v5, 'inlineLiteral' becomes the default.
130
- */
131
- enumType?: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral';
132
- /**
133
- * Suffix appended to the generated type alias name when `enumType` is `asConst` or `asPascalConst`.
134
- *
135
- * Only affects the type alias — the const object name is unchanged.
136
- *
137
- * @default 'Key'
138
- * @example enumTypeSuffix: 'Value' → `export type PetStatusValue = …`
139
- */
140
- enumTypeSuffix?: string;
141
- /**
142
- * Choose the casing for enum key names.
143
- * - 'screamingSnakeCase' generates keys in SCREAMING_SNAKE_CASE format.
144
- * - 'snakeCase' generates keys in snake_case format.
145
- * - 'pascalCase' generates keys in PascalCase format.
146
- * - 'camelCase' generates keys in camelCase format.
147
- * - 'none' uses the enum value as-is without transformation.
148
- * @default 'none'
149
- */
150
- enumKeyCasing?: 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none';
151
201
  /**
152
202
  * Switch between type or interface for creating TypeScript types.
153
203
  * - 'type' generates type alias declarations.
@@ -218,13 +268,13 @@ type Options = {
218
268
  * ```
219
269
  */
220
270
  transformers?: Array<Visitor>;
221
- };
271
+ } & EnumTypeOptions;
222
272
  type ResolvedOptions = {
223
273
  output: Output;
224
274
  group: Group | undefined;
225
275
  enumType: NonNullable<Options['enumType']>;
226
276
  enumTypeSuffix: NonNullable<Options['enumTypeSuffix']>;
227
- enumKeyCasing: NonNullable<Options['enumKeyCasing']>;
277
+ enumKeyCasing: EnumKeyCasing;
228
278
  optionalType: NonNullable<Options['optionalType']>;
229
279
  arrayType: NonNullable<Options['arrayType']>;
230
280
  syntaxType: NonNullable<Options['syntaxType']>;
@@ -280,6 +330,12 @@ type Props = {
280
330
  resolver: PluginTs['resolver'];
281
331
  description?: string;
282
332
  keysToOmit?: string[];
333
+ /**
334
+ * Names of top-level schemas that are enums.
335
+ * Used so the printer's `ref` handler can use the suffixed type name (e.g. `StatusKey`)
336
+ * instead of the plain PascalCase name (e.g. `Status`) when resolving `$ref` enum targets.
337
+ */
338
+ enumSchemaNames?: Set<string>;
283
339
  };
284
340
  declare function Type({
285
341
  name,
@@ -292,7 +348,8 @@ declare function Type({
292
348
  enumTypeSuffix,
293
349
  enumKeyCasing,
294
350
  description,
295
- resolver
351
+ resolver,
352
+ enumSchemaNames
296
353
  }: Props): FabricReactNode;
297
354
  //#endregion
298
355
  //#region src/generators/typeGenerator.d.ts
@@ -411,6 +468,12 @@ type TsOptions = {
411
468
  * Resolver used to transform raw schema names into valid TypeScript identifiers.
412
469
  */
413
470
  resolver: ResolverTs;
471
+ /**
472
+ * Names of top-level schemas that are enums.
473
+ * When set, the `ref` handler uses the suffixed type name (e.g. `StatusKey`) for enum refs
474
+ * instead of the plain PascalCase name, so imports align with what the enum file actually exports.
475
+ */
476
+ enumSchemaNames?: Set<string>;
414
477
  };
415
478
  /**
416
479
  * TypeScript printer factory options: maps `SchemaNode` → `ts.TypeNode` (raw) or `ts.Node` (full declaration).
package/dist/index.js CHANGED
@@ -410,8 +410,7 @@ function getEnumNames({ node, enumType, enumTypeSuffix, resolver }) {
410
410
  const resolved = resolver.default(node.name, "type");
411
411
  return {
412
412
  enumName: enumType === "asPascalConst" ? resolved : camelCase(node.name),
413
- typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved,
414
- refName: resolved
413
+ typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved
415
414
  };
416
415
  }
417
416
  /**
@@ -592,7 +591,7 @@ const printerTs = definePrinter((options) => {
592
591
  ref(node) {
593
592
  if (!node.name) return;
594
593
  const refName = node.ref ? node.ref.split("/").at(-1) ?? node.name : node.name;
595
- return createTypeReferenceNode(node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
594
+ return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enumTypeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
596
595
  },
597
596
  enum(node) {
598
597
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
@@ -692,7 +691,7 @@ const printerTs = definePrinter((options) => {
692
691
  });
693
692
  //#endregion
694
693
  //#region src/components/Type.tsx
695
- function Type({ name, node, keysToOmit, optionalType, arrayType, syntaxType, enumType, enumTypeSuffix, enumKeyCasing, description, resolver }) {
694
+ function Type({ name, node, keysToOmit, optionalType, arrayType, syntaxType, enumType, enumTypeSuffix, enumKeyCasing, description, resolver, enumSchemaNames }) {
696
695
  const resolvedDescription = description || node?.description;
697
696
  const enumSchemaNodes = collect(node, { schema(n) {
698
697
  const enumNode = narrowSchema(n, schemaTypes.enum);
@@ -707,7 +706,8 @@ function Type({ name, node, keysToOmit, optionalType, arrayType, syntaxType, enu
707
706
  syntaxType,
708
707
  description: resolvedDescription,
709
708
  keysToOmit,
710
- resolver
709
+ resolver,
710
+ enumSchemaNames
711
711
  }).print(node);
712
712
  if (!output) return;
713
713
  const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
@@ -864,11 +864,16 @@ const typeGenerator = defineGenerator({
864
864
  group
865
865
  });
866
866
  const params = caseParams(node.parameters, paramsCasing);
867
+ const enumSchemaNames = new Set((adapter.rootNode?.schemas ?? []).filter((s) => narrowSchema(s, schemaTypes.enum) && s.name).map((s) => s.name));
868
+ function resolveImportName(schemaName) {
869
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
870
+ return resolver.default(schemaName, "type");
871
+ }
867
872
  function renderSchemaType({ node: schemaNode, name, description, keysToOmit }) {
868
873
  if (!schemaNode) return null;
869
874
  const transformedNode = transform(schemaNode, composeTransformers(...transformers));
870
875
  const imports = adapter.getImports(transformedNode, (schemaName) => ({
871
- name: resolver.default(schemaName, "type"),
876
+ name: resolveImportName(schemaName),
872
877
  path: resolver.resolveFile({
873
878
  name: schemaName,
874
879
  extname: ".ts"
@@ -898,7 +903,8 @@ const typeGenerator = defineGenerator({
898
903
  arrayType,
899
904
  syntaxType,
900
905
  resolver,
901
- keysToOmit
906
+ keysToOmit,
907
+ enumSchemaNames
902
908
  })] });
903
909
  }
904
910
  const paramTypes = params.map((param) => renderSchemaType({
@@ -970,8 +976,13 @@ const typeGenerator = defineGenerator({
970
976
  const mode = getMode(path.resolve(root, output.path));
971
977
  if (!node.name) return;
972
978
  const transformedNode = transform(node, composeTransformers(...transformers));
979
+ const enumSchemaNames = new Set((adapter.rootNode?.schemas ?? []).filter((s) => narrowSchema(s, schemaTypes.enum) && s.name).map((s) => s.name));
980
+ function resolveImportName(schemaName) {
981
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
982
+ return resolver.default(schemaName, "type");
983
+ }
973
984
  const imports = adapter.getImports(transformedNode, (schemaName) => ({
974
- name: resolver.default(schemaName, "type"),
985
+ name: resolveImportName(schemaName),
975
986
  path: resolver.resolveFile({
976
987
  name: schemaName,
977
988
  extname: ".ts"
@@ -1023,7 +1034,8 @@ const typeGenerator = defineGenerator({
1023
1034
  optionalType,
1024
1035
  arrayType,
1025
1036
  syntaxType,
1026
- resolver
1037
+ resolver,
1038
+ enumSchemaNames
1027
1039
  })]
1028
1040
  });
1029
1041
  }