@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-ts",
3
- "version": "5.0.0-alpha.23",
3
+ "version": "5.0.0-alpha.24",
4
4
  "description": "TypeScript code generation plugin for Kubb, transforming OpenAPI schemas into TypeScript interfaces, types, and utility functions.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -53,8 +53,8 @@
53
53
  "@kubb/react-fabric": "0.15.1",
54
54
  "remeda": "^2.33.6",
55
55
  "typescript": "5.9.3",
56
- "@kubb/ast": "5.0.0-alpha.23",
57
- "@kubb/core": "5.0.0-alpha.23"
56
+ "@kubb/ast": "5.0.0-alpha.24",
57
+ "@kubb/core": "5.0.0-alpha.24"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@kubb/react-fabric": "0.15.1"
@@ -35,17 +35,12 @@ export function getEnumNames({
35
35
  }): {
36
36
  enumName: string
37
37
  typeName: string
38
- /**
39
- * The PascalCase name that `$ref` importers will use to reference this enum type.
40
- * For `asConst`/`asPascalConst` this differs from `typeName` (which has a `Key` suffix).
41
- */
42
- refName: string
43
38
  } {
44
39
  const resolved = resolver.default(node.name!, 'type')
45
40
  const enumName = enumType === 'asPascalConst' ? resolved : camelCase(node.name!)
46
41
  const typeName = ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved
47
42
 
48
- return { enumName, typeName, refName: resolved }
43
+ return { enumName, typeName }
49
44
  }
50
45
 
51
46
  /**
@@ -18,6 +18,12 @@ type Props = {
18
18
  resolver: PluginTs['resolver']
19
19
  description?: string
20
20
  keysToOmit?: string[]
21
+ /**
22
+ * Names of top-level schemas that are enums.
23
+ * Used so the printer's `ref` handler can use the suffixed type name (e.g. `StatusKey`)
24
+ * instead of the plain PascalCase name (e.g. `Status`) when resolving `$ref` enum targets.
25
+ */
26
+ enumSchemaNames?: Set<string>
21
27
  }
22
28
 
23
29
  export function Type({
@@ -32,6 +38,7 @@ export function Type({
32
38
  enumKeyCasing,
33
39
  description,
34
40
  resolver,
41
+ enumSchemaNames,
35
42
  }: Props): FabricReactNode {
36
43
  const resolvedDescription = description || node?.description
37
44
  const enumSchemaNodes = collect<EnumSchemaNode>(node, {
@@ -51,6 +58,7 @@ export function Type({
51
58
  description: resolvedDescription,
52
59
  keysToOmit,
53
60
  resolver,
61
+ enumSchemaNames,
54
62
  })
55
63
  const output = printer.print(node)
56
64
 
@@ -21,6 +21,17 @@ export const typeGenerator = defineGenerator<PluginTs>({
21
21
 
22
22
  const params = caseParams(node.parameters, paramsCasing)
23
23
 
24
+ // Build a set of schema names that are enums so the ref handler and getImports
25
+ // callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
26
+ const enumSchemaNames = new Set((adapter.rootNode?.schemas ?? []).filter((s) => narrowSchema(s, schemaTypes.enum) && s.name).map((s) => s.name!))
27
+
28
+ function resolveImportName(schemaName: string): string {
29
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) {
30
+ return resolver.resolveEnumKeyName({ name: schemaName } as SchemaNode, enumTypeSuffix)
31
+ }
32
+ return resolver.default(schemaName, 'type')
33
+ }
34
+
24
35
  function renderSchemaType({
25
36
  node: schemaNode,
26
37
  name,
@@ -39,7 +50,7 @@ export const typeGenerator = defineGenerator<PluginTs>({
39
50
  const transformedNode = transform(schemaNode, composeTransformers(...transformers))
40
51
 
41
52
  const imports = adapter.getImports(transformedNode, (schemaName) => ({
42
- name: resolver.default(schemaName, 'type'),
53
+ name: resolveImportName(schemaName),
43
54
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
44
55
  }))
45
56
 
@@ -59,6 +70,7 @@ export const typeGenerator = defineGenerator<PluginTs>({
59
70
  syntaxType={syntaxType}
60
71
  resolver={resolver}
61
72
  keysToOmit={keysToOmit}
73
+ enumSchemaNames={enumSchemaNames}
62
74
  />
63
75
  </>
64
76
  )
@@ -134,8 +146,19 @@ export const typeGenerator = defineGenerator<PluginTs>({
134
146
 
135
147
  const transformedNode = transform(node, composeTransformers(...transformers))
136
148
 
149
+ // Build a set of schema names that are enums so the ref handler and getImports
150
+ // callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
151
+ const enumSchemaNames = new Set((adapter.rootNode?.schemas ?? []).filter((s) => narrowSchema(s, schemaTypes.enum) && s.name).map((s) => s.name!))
152
+
153
+ function resolveImportName(schemaName: string): string {
154
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) {
155
+ return resolver.resolveEnumKeyName({ name: schemaName } as SchemaNode, enumTypeSuffix)
156
+ }
157
+ return resolver.default(schemaName, 'type')
158
+ }
159
+
137
160
  const imports = adapter.getImports(transformedNode, (schemaName) => ({
138
- name: resolver.default(schemaName, 'type'),
161
+ name: resolveImportName(schemaName),
139
162
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
140
163
  }))
141
164
 
@@ -170,6 +193,7 @@ export const typeGenerator = defineGenerator<PluginTs>({
170
193
  arrayType={arrayType}
171
194
  syntaxType={syntaxType}
172
195
  resolver={resolver}
196
+ enumSchemaNames={enumSchemaNames}
173
197
  />
174
198
  </File>
175
199
  )
@@ -52,6 +52,12 @@ type TsOptions = {
52
52
  * Resolver used to transform raw schema names into valid TypeScript identifiers.
53
53
  */
54
54
  resolver: ResolverTs
55
+ /**
56
+ * Names of top-level schemas that are enums.
57
+ * When set, the `ref` handler uses the suffixed type name (e.g. `StatusKey`) for enum refs
58
+ * instead of the plain PascalCase name, so imports align with what the enum file actually exports.
59
+ */
60
+ enumSchemaNames?: Set<string>
55
61
  }
56
62
 
57
63
  /**
@@ -254,7 +260,17 @@ export const printerTs = definePrinter<TsPrinter>((options) => {
254
260
  // (e.g. by single-member allOf flatten using the property-derived child name).
255
261
  // Inline refs (without $ref) from utils already carry resolved type names.
256
262
  const refName = node.ref ? (node.ref.split('/').at(-1) ?? node.name) : node.name
257
- const name = node.ref ? this.options.resolver.default(refName, 'type') : refName
263
+
264
+ // When a Key suffix is configured, enum refs must use the suffixed name (e.g. `StatusKey`)
265
+ // so the reference matches what the enum file actually exports.
266
+ const isEnumRef =
267
+ node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix && this.options.enumSchemaNames?.has(refName)
268
+
269
+ const name = isEnumRef
270
+ ? this.options.resolver.resolveEnumKeyName({ name: refName } as SchemaNode, this.options.enumTypeSuffix!)
271
+ : node.ref
272
+ ? this.options.resolver.default(refName, 'type')
273
+ : refName
258
274
 
259
275
  return factory.createTypeReferenceNode(name, undefined)
260
276
  },
package/src/types.ts CHANGED
@@ -100,6 +100,92 @@ export type ResolverTs = Resolver &
100
100
  resolveHeaderParamsName(node: OperationNode, param: ParameterNode): string
101
101
  }
102
102
 
103
+ type EnumKeyCasing = 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none'
104
+
105
+ /**
106
+ * Discriminated union that ties `enumTypeSuffix` and `enumKeyCasing` to the enum types that actually use them.
107
+ *
108
+ * - `'asConst'` / `'asPascalConst'` — emit a `const` object; both `enumTypeSuffix` (type-alias suffix) and
109
+ * `enumKeyCasing` (key formatting) are meaningful.
110
+ * - `'enum'` / `'constEnum'` — emit a TypeScript enum; `enumKeyCasing` applies to member names,
111
+ * but there is no separate type alias so `enumTypeSuffix` is not used.
112
+ * - `'literal'` / `'inlineLiteral'` — emit only union literals; keys are discarded entirely,
113
+ * so neither `enumTypeSuffix` nor `enumKeyCasing` have any effect.
114
+ */
115
+ type EnumTypeOptions =
116
+ | {
117
+ /**
118
+ * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
119
+ * - 'asConst' generates const objects with camelCase names and as const assertion.
120
+ * - 'asPascalConst' generates const objects with PascalCase names and as const assertion.
121
+ * @default 'asConst'
122
+ */
123
+ enumType?: 'asConst' | 'asPascalConst'
124
+ /**
125
+ * Suffix appended to the generated type alias name.
126
+ *
127
+ * Only affects the type alias — the const object name is unchanged.
128
+ *
129
+ * @default 'Key'
130
+ * @example enumTypeSuffix: 'Value' → `export type PetStatusValue = …`
131
+ */
132
+ enumTypeSuffix?: string
133
+ /**
134
+ * Choose the casing for enum key names.
135
+ * - 'screamingSnakeCase' generates keys in SCREAMING_SNAKE_CASE format.
136
+ * - 'snakeCase' generates keys in snake_case format.
137
+ * - 'pascalCase' generates keys in PascalCase format.
138
+ * - 'camelCase' generates keys in camelCase format.
139
+ * - 'none' uses the enum value as-is without transformation.
140
+ * @default 'none'
141
+ */
142
+ enumKeyCasing?: EnumKeyCasing
143
+ }
144
+ | {
145
+ /**
146
+ * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
147
+ * - 'enum' generates TypeScript enum declarations.
148
+ * - 'constEnum' generates TypeScript const enum declarations.
149
+ * @default 'asConst'
150
+ */
151
+ enumType?: 'enum' | 'constEnum'
152
+ /**
153
+ * `enumTypeSuffix` has no effect for this `enumType`.
154
+ * It is only used when `enumType` is `'asConst'` or `'asPascalConst'`.
155
+ */
156
+ enumTypeSuffix?: never
157
+ /**
158
+ * Choose the casing for enum key names.
159
+ * - 'screamingSnakeCase' generates keys in SCREAMING_SNAKE_CASE format.
160
+ * - 'snakeCase' generates keys in snake_case format.
161
+ * - 'pascalCase' generates keys in PascalCase format.
162
+ * - 'camelCase' generates keys in camelCase format.
163
+ * - 'none' uses the enum value as-is without transformation.
164
+ * @default 'none'
165
+ */
166
+ enumKeyCasing?: EnumKeyCasing
167
+ }
168
+ | {
169
+ /**
170
+ * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
171
+ * - 'literal' generates literal union types.
172
+ * - 'inlineLiteral' inlines enum values directly into the type (default in v5).
173
+ * @default 'asConst'
174
+ * @note In Kubb v5, 'inlineLiteral' becomes the default.
175
+ */
176
+ enumType?: 'literal' | 'inlineLiteral'
177
+ /**
178
+ * `enumTypeSuffix` has no effect for this `enumType`.
179
+ * It is only used when `enumType` is `'asConst'` or `'asPascalConst'`.
180
+ */
181
+ enumTypeSuffix?: never
182
+ /**
183
+ * `enumKeyCasing` has no effect for this `enumType`.
184
+ * Literal and inlineLiteral modes emit only values — keys are discarded entirely.
185
+ */
186
+ enumKeyCasing?: never
187
+ }
188
+
103
189
  export type Options = {
104
190
  /**
105
191
  * Specify the export location for the files and define the behavior of the output
@@ -127,37 +213,6 @@ export type Options = {
127
213
  * Array containing override parameters to override `options` based on tags/operations/methods/paths.
128
214
  */
129
215
  override?: Array<Override<ResolvedOptions>>
130
- /**
131
- * Choose to use enum, asConst, asPascalConst, constEnum, literal, or inlineLiteral for enums.
132
- * - 'enum' generates TypeScript enum declarations.
133
- * - 'asConst' generates const objects with camelCase names and as const assertion.
134
- * - 'asPascalConst' generates const objects with PascalCase names and as const assertion.
135
- * - 'constEnum' generates TypeScript const enum declarations.
136
- * - 'literal' generates literal union types.
137
- * - 'inlineLiteral' inline enum values directly into the type (default in v5).
138
- * @default 'asConst'
139
- * @note In Kubb v5, 'inlineLiteral' becomes the default.
140
- */
141
- enumType?: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral'
142
- /**
143
- * Suffix appended to the generated type alias name when `enumType` is `asConst` or `asPascalConst`.
144
- *
145
- * Only affects the type alias — the const object name is unchanged.
146
- *
147
- * @default 'Key'
148
- * @example enumTypeSuffix: 'Value' → `export type PetStatusValue = …`
149
- */
150
- enumTypeSuffix?: string
151
- /**
152
- * Choose the casing for enum key names.
153
- * - 'screamingSnakeCase' generates keys in SCREAMING_SNAKE_CASE format.
154
- * - 'snakeCase' generates keys in snake_case format.
155
- * - 'pascalCase' generates keys in PascalCase format.
156
- * - 'camelCase' generates keys in camelCase format.
157
- * - 'none' uses the enum value as-is without transformation.
158
- * @default 'none'
159
- */
160
- enumKeyCasing?: 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none'
161
216
  /**
162
217
  * Switch between type or interface for creating TypeScript types.
163
218
  * - 'type' generates type alias declarations.
@@ -228,14 +283,14 @@ export type Options = {
228
283
  * ```
229
284
  */
230
285
  transformers?: Array<Visitor>
231
- }
286
+ } & EnumTypeOptions
232
287
 
233
288
  type ResolvedOptions = {
234
289
  output: Output
235
290
  group: Group | undefined
236
291
  enumType: NonNullable<Options['enumType']>
237
292
  enumTypeSuffix: NonNullable<Options['enumTypeSuffix']>
238
- enumKeyCasing: NonNullable<Options['enumKeyCasing']>
293
+ enumKeyCasing: EnumKeyCasing
239
294
  optionalType: NonNullable<Options['optionalType']>
240
295
  arrayType: NonNullable<Options['arrayType']>
241
296
  syntaxType: NonNullable<Options['syntaxType']>