@kubb/plugin-ts 5.0.0-alpha.11 → 5.0.0-alpha.12

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.
Files changed (43) hide show
  1. package/dist/{components-CRu8IKY3.js → Type-CX1HRooG.js} +377 -365
  2. package/dist/Type-CX1HRooG.js.map +1 -0
  3. package/dist/Type-Cat0_htq.cjs +808 -0
  4. package/dist/Type-Cat0_htq.cjs.map +1 -0
  5. package/dist/components.cjs +3 -2
  6. package/dist/components.d.ts +40 -9
  7. package/dist/components.js +2 -2
  8. package/dist/generators-CLuCmfUz.js +532 -0
  9. package/dist/generators-CLuCmfUz.js.map +1 -0
  10. package/dist/generators-DWBU-MuW.cjs +536 -0
  11. package/dist/generators-DWBU-MuW.cjs.map +1 -0
  12. package/dist/generators.cjs +2 -3
  13. package/dist/generators.d.ts +3 -503
  14. package/dist/generators.js +2 -2
  15. package/dist/index.cjs +308 -4
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.ts +26 -21
  18. package/dist/index.js +305 -2
  19. package/dist/index.js.map +1 -0
  20. package/dist/{types-mSXmB8WU.d.ts → types-BA1ZCQ5p.d.ts} +73 -57
  21. package/package.json +5 -5
  22. package/src/components/{v2/Enum.tsx → Enum.tsx} +27 -11
  23. package/src/components/Type.tsx +23 -141
  24. package/src/components/index.ts +1 -0
  25. package/src/generators/index.ts +0 -1
  26. package/src/generators/typeGenerator.tsx +189 -413
  27. package/src/generators/utils.ts +298 -0
  28. package/src/index.ts +1 -1
  29. package/src/plugin.ts +80 -126
  30. package/src/printer.ts +15 -4
  31. package/src/resolverTs.ts +109 -1
  32. package/src/types.ts +68 -52
  33. package/dist/components-CRu8IKY3.js.map +0 -1
  34. package/dist/components-DeNDKlzf.cjs +0 -982
  35. package/dist/components-DeNDKlzf.cjs.map +0 -1
  36. package/dist/plugin-CJ29AwE2.cjs +0 -1320
  37. package/dist/plugin-CJ29AwE2.cjs.map +0 -1
  38. package/dist/plugin-D60XNJSD.js +0 -1267
  39. package/dist/plugin-D60XNJSD.js.map +0 -1
  40. package/src/components/v2/Type.tsx +0 -59
  41. package/src/generators/v2/typeGenerator.tsx +0 -167
  42. package/src/generators/v2/utils.ts +0 -140
  43. package/src/parser.ts +0 -389
package/src/parser.ts DELETED
@@ -1,389 +0,0 @@
1
- import { jsStringEscape } from '@internals/utils'
2
- import type { SchemaKeywordMapper, SchemaMapper } from '@kubb/plugin-oas'
3
- import { createParser, isKeyword, schemaKeywords } from '@kubb/plugin-oas'
4
- import type ts from 'typescript'
5
- import * as factory from './factory.ts'
6
-
7
- export const typeKeywordMapper = {
8
- any: () => factory.keywordTypeNodes.any,
9
- unknown: () => factory.keywordTypeNodes.unknown,
10
- void: () => factory.keywordTypeNodes.void,
11
- number: () => factory.keywordTypeNodes.number,
12
- integer: () => factory.keywordTypeNodes.number,
13
- bigint: () => factory.keywordTypeNodes.bigint,
14
- object: (nodes?: ts.TypeElement[]) => {
15
- if (!nodes || !nodes.length) {
16
- return factory.keywordTypeNodes.object
17
- }
18
-
19
- return factory.createTypeLiteralNode(nodes)
20
- },
21
- string: () => factory.keywordTypeNodes.string,
22
- boolean: () => factory.keywordTypeNodes.boolean,
23
- undefined: () => factory.keywordTypeNodes.undefined,
24
- nullable: undefined,
25
- null: () => factory.keywordTypeNodes.null,
26
- nullish: undefined,
27
- array: (nodes?: ts.TypeNode[], arrayType?: 'array' | 'generic') => {
28
- if (!nodes) {
29
- return undefined
30
- }
31
-
32
- return factory.createArrayDeclaration({ nodes, arrayType })
33
- },
34
- tuple: (nodes?: ts.TypeNode[], rest?: ts.TypeNode, min?: number, max?: number) => {
35
- if (!nodes) {
36
- return undefined
37
- }
38
-
39
- if (max) {
40
- nodes = nodes.slice(0, max)
41
-
42
- if (nodes.length < max && rest) {
43
- nodes = [...nodes, ...Array(max - nodes.length).fill(rest)]
44
- }
45
- }
46
-
47
- if (min) {
48
- nodes = nodes.map((node, index) => (index >= min ? factory.createOptionalTypeNode(node) : node))
49
- }
50
-
51
- if (typeof max === 'undefined' && rest) {
52
- nodes.push(factory.createRestTypeNode(factory.createArrayTypeNode(rest)))
53
- }
54
-
55
- return factory.createTupleTypeNode(nodes)
56
- },
57
- enum: (name?: string) => {
58
- if (!name) {
59
- return undefined
60
- }
61
-
62
- return factory.createTypeReferenceNode(name, undefined)
63
- },
64
- union: (nodes?: ts.TypeNode[]) => {
65
- if (!nodes) {
66
- return undefined
67
- }
68
-
69
- return factory.createUnionDeclaration({
70
- withParentheses: true,
71
- nodes,
72
- })
73
- },
74
- const: (name?: string | number | boolean, format?: 'string' | 'number' | 'boolean') => {
75
- if (name === null || name === undefined || name === '') {
76
- return undefined
77
- }
78
-
79
- if (format === 'boolean') {
80
- if (name === true) {
81
- return factory.createLiteralTypeNode(factory.createTrue())
82
- }
83
-
84
- return factory.createLiteralTypeNode(factory.createFalse())
85
- }
86
-
87
- if (format === 'number' && typeof name === 'number') {
88
- return factory.createLiteralTypeNode(factory.createNumericLiteral(name))
89
- }
90
-
91
- return factory.createLiteralTypeNode(factory.createStringLiteral(name.toString()))
92
- },
93
- datetime: () => factory.keywordTypeNodes.string,
94
- date: (type: 'date' | 'string' = 'string') =>
95
- type === 'string' ? factory.keywordTypeNodes.string : factory.createTypeReferenceNode(factory.createIdentifier('Date')),
96
- time: (type: 'date' | 'string' = 'string') =>
97
- type === 'string' ? factory.keywordTypeNodes.string : factory.createTypeReferenceNode(factory.createIdentifier('Date')),
98
- uuid: () => factory.keywordTypeNodes.string,
99
- url: () => factory.keywordTypeNodes.string,
100
- default: undefined,
101
- and: (nodes?: ts.TypeNode[]) => {
102
- if (!nodes) {
103
- return undefined
104
- }
105
-
106
- return factory.createIntersectionDeclaration({
107
- withParentheses: true,
108
- nodes,
109
- })
110
- },
111
- describe: undefined,
112
- min: undefined,
113
- max: undefined,
114
- optional: undefined,
115
- matches: () => factory.keywordTypeNodes.string,
116
- email: () => factory.keywordTypeNodes.string,
117
- firstName: undefined,
118
- lastName: undefined,
119
- password: undefined,
120
- phone: undefined,
121
- readOnly: undefined,
122
- writeOnly: undefined,
123
- ref: (propertyName?: string) => {
124
- if (!propertyName) {
125
- return undefined
126
- }
127
-
128
- return factory.createTypeReferenceNode(propertyName, undefined)
129
- },
130
- blob: () => factory.createTypeReferenceNode('Blob', []),
131
- deprecated: undefined,
132
- example: undefined,
133
- schema: undefined,
134
- catchall: undefined,
135
- name: undefined,
136
- interface: undefined,
137
- exclusiveMaximum: undefined,
138
- exclusiveMinimum: undefined,
139
- } satisfies SchemaMapper<ts.TypeNode | null | undefined>
140
-
141
- type ParserOptions = {
142
- /**
143
- * @default `'questionToken'`
144
- */
145
- optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined'
146
- /**
147
- * @default `'array'`
148
- */
149
- arrayType: 'array' | 'generic'
150
- /**
151
- * Choose to use `enum`, `asConst`, `asPascalConst`, `constEnum`, `literal`, or `inlineLiteral` for enums.
152
- * - `enum`: TypeScript enum
153
- * - `asConst`: const with camelCase name (e.g., `petType`)
154
- * - `asPascalConst`: const with PascalCase name (e.g., `PetType`)
155
- * - `constEnum`: const enum
156
- * - `literal`: literal union type
157
- * - `inlineLiteral`: inline enum values directly into the type (default in v5)
158
- * @default `'asConst'`
159
- * @note In Kubb v5, `inlineLiteral` becomes the default.
160
- */
161
- enumType: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral'
162
- }
163
-
164
- /**
165
- * Recursively parses a schema tree node into a corresponding TypeScript AST node.
166
- *
167
- * Maps OpenAPI schema keywords to TypeScript AST nodes using the `typeKeywordMapper`, handling complex types such as unions, intersections, arrays, tuples (with optional/rest elements and length constraints), enums, constants, references, and objects with property modifiers and documentation annotations.
168
- *
169
- * @param current - The schema node to parse.
170
- * @param siblings - Sibling schema nodes, used for context in certain mappings.
171
- * @param name - The name of the schema or property being parsed.
172
- * @param options - Parsing options controlling output style, property handling.
173
- * @returns The generated TypeScript AST node, or `undefined` if the schema keyword is not mapped.
174
- */
175
- export const parse = createParser<ts.Node | null, ParserOptions>({
176
- mapper: typeKeywordMapper,
177
- handlers: {
178
- union(tree, options) {
179
- const { current, schema, name } = tree
180
-
181
- return typeKeywordMapper.union(
182
- current.args.map((it) => this.parse({ schema, parent: current, name, current: it, siblings: [] }, options)).filter(Boolean) as ts.TypeNode[],
183
- )
184
- },
185
- and(tree, options) {
186
- const { current, schema, name } = tree
187
-
188
- return typeKeywordMapper.and(
189
- current.args.map((it) => this.parse({ schema, parent: current, name, current: it, siblings: [] }, options)).filter(Boolean) as ts.TypeNode[],
190
- )
191
- },
192
- array(tree, options) {
193
- const { current, schema, name } = tree
194
-
195
- return typeKeywordMapper.array(
196
- current.args.items.map((it) => this.parse({ schema, parent: current, name, current: it, siblings: [] }, options)).filter(Boolean) as ts.TypeNode[],
197
- options.arrayType,
198
- )
199
- },
200
- enum(tree, options) {
201
- const { current } = tree
202
-
203
- // If enumType is 'inlineLiteral', generate the literal union inline instead of a type reference
204
- if (options.enumType === 'inlineLiteral') {
205
- const enumValues = current.args.items
206
- .map((item) => item.value)
207
- .filter((value): value is string | number | boolean => value !== undefined && value !== null)
208
- .map((value) => {
209
- const format = typeof value === 'number' ? 'number' : typeof value === 'boolean' ? 'boolean' : 'string'
210
- return typeKeywordMapper.const(value, format)
211
- })
212
- .filter(Boolean) as ts.TypeNode[]
213
-
214
- return typeKeywordMapper.union(enumValues)
215
- }
216
-
217
- // Adding suffix to enum (see https://github.com/kubb-labs/kubb/issues/1873)
218
- return typeKeywordMapper.enum(['asConst', 'asPascalConst'].includes(options.enumType) ? `${current.args.typeName}Key` : current.args.typeName)
219
- },
220
- ref(tree, _options) {
221
- const { current } = tree
222
-
223
- return typeKeywordMapper.ref(current.args.name)
224
- },
225
- blob() {
226
- return typeKeywordMapper.blob()
227
- },
228
- tuple(tree, options) {
229
- const { current, schema, name } = tree
230
-
231
- return typeKeywordMapper.tuple(
232
- current.args.items.map((it) => this.parse({ schema, parent: current, name, current: it, siblings: [] }, options)).filter(Boolean) as ts.TypeNode[],
233
- current.args.rest &&
234
- ((this.parse({ schema, parent: current, name, current: current.args.rest, siblings: [] }, options) ?? undefined) as ts.TypeNode | undefined),
235
- current.args.min,
236
- current.args.max,
237
- )
238
- },
239
- const(tree, _options) {
240
- const { current } = tree
241
-
242
- return typeKeywordMapper.const(current.args.name, current.args.format)
243
- },
244
- object(tree, options) {
245
- const { current, schema, name } = tree
246
-
247
- const properties = Object.entries(current.args?.properties || {})
248
- .filter((item) => {
249
- const schemas = item[1]
250
- return schemas && typeof schemas.map === 'function'
251
- })
252
- .map(([name, schemas]) => {
253
- const nameSchema = schemas.find((schema) => schema.keyword === schemaKeywords.name) as SchemaKeywordMapper['name']
254
- const mappedName = nameSchema?.args || name
255
-
256
- const isNullish = schemas.some((schema) => schema.keyword === schemaKeywords.nullish)
257
- const isNullable = schemas.some((schema) => schema.keyword === schemaKeywords.nullable)
258
- const isOptional = schemas.some((schema) => schema.keyword === schemaKeywords.optional)
259
- const isReadonly = schemas.some((schema) => schema.keyword === schemaKeywords.readOnly)
260
- const describeSchema = schemas.find((schema) => schema.keyword === schemaKeywords.describe) as SchemaKeywordMapper['describe'] | undefined
261
- const deprecatedSchema = schemas.find((schema) => schema.keyword === schemaKeywords.deprecated) as SchemaKeywordMapper['deprecated'] | undefined
262
- const defaultSchema = schemas.find((schema) => schema.keyword === schemaKeywords.default) as SchemaKeywordMapper['default'] | undefined
263
- const exampleSchema = schemas.find((schema) => schema.keyword === schemaKeywords.example) as SchemaKeywordMapper['example'] | undefined
264
- const schemaSchema = schemas.find((schema) => schema.keyword === schemaKeywords.schema) as SchemaKeywordMapper['schema'] | undefined
265
- const minSchema = schemas.find((schema) => schema.keyword === schemaKeywords.min) as SchemaKeywordMapper['min'] | undefined
266
- const maxSchema = schemas.find((schema) => schema.keyword === schemaKeywords.max) as SchemaKeywordMapper['max'] | undefined
267
- const matchesSchema = schemas.find((schema) => schema.keyword === schemaKeywords.matches) as SchemaKeywordMapper['matches'] | undefined
268
-
269
- let type = schemas
270
- .map((it) =>
271
- this.parse(
272
- {
273
- schema,
274
- parent: current,
275
- name,
276
- current: it,
277
- siblings: schemas,
278
- },
279
- options,
280
- ),
281
- )
282
- .filter(Boolean)[0] as ts.TypeNode
283
-
284
- if (isNullable) {
285
- type = factory.createUnionDeclaration({
286
- nodes: [type, factory.keywordTypeNodes.null],
287
- }) as ts.TypeNode
288
- }
289
-
290
- if (isNullish && ['undefined', 'questionTokenAndUndefined'].includes(options.optionalType as string)) {
291
- type = factory.createUnionDeclaration({
292
- nodes: [type, factory.keywordTypeNodes.undefined],
293
- }) as ts.TypeNode
294
- }
295
-
296
- if (isOptional && ['undefined', 'questionTokenAndUndefined'].includes(options.optionalType as string)) {
297
- type = factory.createUnionDeclaration({
298
- nodes: [type, factory.keywordTypeNodes.undefined],
299
- }) as ts.TypeNode
300
- }
301
-
302
- const propertyNode = factory.createPropertySignature({
303
- questionToken: isOptional || isNullish ? ['questionToken', 'questionTokenAndUndefined'].includes(options.optionalType as string) : false,
304
- name: mappedName,
305
- type,
306
- readOnly: isReadonly,
307
- })
308
-
309
- return factory.appendJSDocToNode({
310
- node: propertyNode,
311
- comments: [
312
- describeSchema ? `@description ${jsStringEscape(describeSchema.args)}` : undefined,
313
- deprecatedSchema ? '@deprecated' : undefined,
314
- minSchema ? `@minLength ${minSchema.args}` : undefined,
315
- maxSchema ? `@maxLength ${maxSchema.args}` : undefined,
316
- matchesSchema ? `@pattern ${matchesSchema.args}` : undefined,
317
- defaultSchema ? `@default ${defaultSchema.args}` : undefined,
318
- exampleSchema ? `@example ${exampleSchema.args}` : undefined,
319
- schemaSchema?.args?.type || schemaSchema?.args?.format
320
- ? [`@type ${schemaSchema?.args?.type || 'unknown'}${!isOptional ? '' : ' | undefined'}`, schemaSchema?.args?.format].filter(Boolean).join(', ')
321
- : undefined,
322
- ].filter(Boolean),
323
- })
324
- })
325
-
326
- let additionalProperties: any
327
-
328
- if (current.args?.additionalProperties?.length) {
329
- let additionalPropertiesType = current.args.additionalProperties
330
- .map((it) => this.parse({ schema, parent: current, name, current: it, siblings: [] }, options))
331
- .filter(Boolean)
332
- .at(0) as ts.TypeNode
333
-
334
- const isNullable = current.args?.additionalProperties.some((schema) => isKeyword(schema, schemaKeywords.nullable))
335
- if (isNullable) {
336
- additionalPropertiesType = factory.createUnionDeclaration({
337
- nodes: [additionalPropertiesType, factory.keywordTypeNodes.null],
338
- }) as ts.TypeNode
339
- }
340
-
341
- // When there are typed properties alongside additionalProperties, use 'unknown' type
342
- // for the index signature to avoid TS2411 errors (index signature type conflicts with property types).
343
- // This occurs commonly in QueryParams where some params are typed (enums, objects) and
344
- // others are dynamic (additionalProperties with explode=true).
345
- const hasTypedProperties = properties.length > 0
346
- const indexSignatureType = hasTypedProperties ? factory.keywordTypeNodes.unknown : additionalPropertiesType
347
-
348
- additionalProperties = factory.createIndexSignature(indexSignatureType)
349
- }
350
-
351
- let patternProperties: ts.TypeNode | ts.IndexSignatureDeclaration | undefined
352
-
353
- if (current.args?.patternProperties) {
354
- const allPatternSchemas = Object.values(current.args.patternProperties).flat()
355
-
356
- if (allPatternSchemas.length > 0) {
357
- patternProperties = allPatternSchemas
358
- .map((it) => this.parse({ schema, parent: current, name, current: it, siblings: [] }, options))
359
- .filter(Boolean)
360
- .at(0) as ts.TypeNode
361
-
362
- const isNullable = allPatternSchemas.some((schema) => isKeyword(schema, schemaKeywords.nullable))
363
- if (isNullable) {
364
- patternProperties = factory.createUnionDeclaration({
365
- nodes: [patternProperties, factory.keywordTypeNodes.null],
366
- }) as ts.TypeNode
367
- }
368
-
369
- patternProperties = factory.createIndexSignature(patternProperties)
370
- }
371
- }
372
-
373
- return typeKeywordMapper.object([...properties, additionalProperties, patternProperties].filter(Boolean))
374
- },
375
- datetime() {
376
- return typeKeywordMapper.datetime()
377
- },
378
- date(tree) {
379
- const { current } = tree
380
-
381
- return typeKeywordMapper.date(current.args.type)
382
- },
383
- time(tree) {
384
- const { current } = tree
385
-
386
- return typeKeywordMapper.time(current.args.type)
387
- },
388
- },
389
- })