@kubb/plugin-ts 5.0.0-alpha.9 → 5.0.0-beta.100

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 (41) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +38 -23
  3. package/dist/index.cjs +2376 -5
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +941 -40
  6. package/dist/index.js +2321 -2
  7. package/dist/index.js.map +1 -0
  8. package/dist/rolldown-runtime-CNktS9qV.js +17 -0
  9. package/package.json +38 -74
  10. package/dist/chunk--u3MIqq1.js +0 -8
  11. package/dist/components-CRu8IKY3.js +0 -729
  12. package/dist/components-CRu8IKY3.js.map +0 -1
  13. package/dist/components-DeNDKlzf.cjs +0 -982
  14. package/dist/components-DeNDKlzf.cjs.map +0 -1
  15. package/dist/components.cjs +0 -3
  16. package/dist/components.d.ts +0 -36
  17. package/dist/components.js +0 -2
  18. package/dist/generators.cjs +0 -4
  19. package/dist/generators.d.ts +0 -509
  20. package/dist/generators.js +0 -2
  21. package/dist/plugin-BZkBwnEA.js +0 -1269
  22. package/dist/plugin-BZkBwnEA.js.map +0 -1
  23. package/dist/plugin-Bunz1oGa.cjs +0 -1322
  24. package/dist/plugin-Bunz1oGa.cjs.map +0 -1
  25. package/dist/types-mSXmB8WU.d.ts +0 -298
  26. package/src/components/Type.tsx +0 -190
  27. package/src/components/index.ts +0 -1
  28. package/src/components/v2/Enum.tsx +0 -67
  29. package/src/components/v2/Type.tsx +0 -59
  30. package/src/constants.ts +0 -29
  31. package/src/factory.ts +0 -726
  32. package/src/generators/index.ts +0 -2
  33. package/src/generators/typeGenerator.tsx +0 -468
  34. package/src/generators/v2/typeGenerator.tsx +0 -167
  35. package/src/generators/v2/utils.ts +0 -140
  36. package/src/index.ts +0 -3
  37. package/src/parser.ts +0 -389
  38. package/src/plugin.ts +0 -232
  39. package/src/printer.ts +0 -368
  40. package/src/resolverTs.ts +0 -77
  41. package/src/types.ts +0 -296
package/src/factory.ts DELETED
@@ -1,726 +0,0 @@
1
- import { camelCase, pascalCase, screamingSnakeCase, snakeCase } from '@internals/utils'
2
- import { isNumber, sortBy } from 'remeda'
3
- import ts from 'typescript'
4
-
5
- const { SyntaxKind, factory } = ts
6
-
7
- // https://ts-ast-viewer.com/
8
-
9
- export const modifiers = {
10
- async: factory.createModifier(ts.SyntaxKind.AsyncKeyword),
11
- export: factory.createModifier(ts.SyntaxKind.ExportKeyword),
12
- const: factory.createModifier(ts.SyntaxKind.ConstKeyword),
13
- static: factory.createModifier(ts.SyntaxKind.StaticKeyword),
14
- } as const
15
-
16
- export const syntaxKind = {
17
- union: SyntaxKind.UnionType as 192,
18
- literalType: SyntaxKind.LiteralType,
19
- stringLiteral: SyntaxKind.StringLiteral,
20
- } as const
21
-
22
- export function getUnknownType(unknownType: 'any' | 'unknown' | 'void' | undefined) {
23
- if (unknownType === 'any') {
24
- return keywordTypeNodes.any
25
- }
26
- if (unknownType === 'void') {
27
- return keywordTypeNodes.void
28
- }
29
-
30
- return keywordTypeNodes.unknown
31
- }
32
- function isValidIdentifier(str: string): boolean {
33
- if (!str.length || str.trim() !== str) {
34
- return false
35
- }
36
- const node = ts.parseIsolatedEntityName(str, ts.ScriptTarget.Latest)
37
-
38
- return !!node && node.kind === ts.SyntaxKind.Identifier && ts.identifierToKeywordKind(node.kind as unknown as ts.Identifier) === undefined
39
- }
40
-
41
- function propertyName(name: string | ts.PropertyName): ts.PropertyName {
42
- if (typeof name === 'string') {
43
- const isValid = isValidIdentifier(name)
44
- return isValid ? factory.createIdentifier(name) : factory.createStringLiteral(name)
45
- }
46
- return name
47
- }
48
-
49
- const questionToken = factory.createToken(ts.SyntaxKind.QuestionToken)
50
-
51
- export function createQuestionToken(token?: boolean | ts.QuestionToken) {
52
- if (!token) {
53
- return undefined
54
- }
55
- if (token === true) {
56
- return questionToken
57
- }
58
- return token
59
- }
60
-
61
- export function createIntersectionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
62
- if (!nodes.length) {
63
- return null
64
- }
65
-
66
- if (nodes.length === 1) {
67
- return nodes[0] || null
68
- }
69
-
70
- const node = factory.createIntersectionTypeNode(nodes)
71
-
72
- if (withParentheses) {
73
- return factory.createParenthesizedType(node)
74
- }
75
-
76
- return node
77
- }
78
-
79
- /**
80
- * Minimum nodes length of 2
81
- * @example `string & number`
82
- */
83
- export function createTupleDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
84
- if (!nodes.length) {
85
- return null
86
- }
87
-
88
- if (nodes.length === 1) {
89
- return nodes[0] || null
90
- }
91
-
92
- const node = factory.createTupleTypeNode(nodes)
93
-
94
- if (withParentheses) {
95
- return factory.createParenthesizedType(node)
96
- }
97
-
98
- return node
99
- }
100
-
101
- export function createArrayDeclaration({ nodes, arrayType = 'array' }: { nodes: Array<ts.TypeNode>; arrayType?: 'array' | 'generic' }): ts.TypeNode | null {
102
- if (!nodes.length) {
103
- return factory.createTupleTypeNode([])
104
- }
105
-
106
- if (nodes.length === 1) {
107
- const node = nodes[0]
108
- if (!node) {
109
- return null
110
- }
111
- if (arrayType === 'generic') {
112
- return factory.createTypeReferenceNode(factory.createIdentifier('Array'), [node])
113
- }
114
- return factory.createArrayTypeNode(node)
115
- }
116
-
117
- // For union types (multiple nodes), respect arrayType preference
118
- const unionType = factory.createUnionTypeNode(nodes)
119
- if (arrayType === 'generic') {
120
- return factory.createTypeReferenceNode(factory.createIdentifier('Array'), [unionType])
121
- }
122
- // For array syntax with unions, we need parentheses: (string | number)[]
123
- return factory.createArrayTypeNode(factory.createParenthesizedType(unionType))
124
- }
125
-
126
- /**
127
- * Minimum nodes length of 2
128
- * @example `string | number`
129
- */
130
- export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode {
131
- if (!nodes.length) {
132
- return keywordTypeNodes.any
133
- }
134
-
135
- if (nodes.length === 1) {
136
- return nodes[0] as ts.TypeNode
137
- }
138
-
139
- const node = factory.createUnionTypeNode(nodes)
140
-
141
- if (withParentheses) {
142
- return factory.createParenthesizedType(node)
143
- }
144
-
145
- return node
146
- }
147
-
148
- export function createPropertySignature({
149
- readOnly,
150
- modifiers = [],
151
- name,
152
- questionToken,
153
- type,
154
- }: {
155
- readOnly?: boolean
156
- modifiers?: Array<ts.Modifier>
157
- name: ts.PropertyName | string
158
- questionToken?: ts.QuestionToken | boolean
159
- type?: ts.TypeNode
160
- }) {
161
- return factory.createPropertySignature(
162
- [...modifiers, readOnly ? factory.createToken(ts.SyntaxKind.ReadonlyKeyword) : undefined].filter(Boolean),
163
- propertyName(name),
164
- createQuestionToken(questionToken),
165
- type,
166
- )
167
- }
168
-
169
- export function createParameterSignature(
170
- name: string | ts.BindingName,
171
- {
172
- modifiers,
173
- dotDotDotToken,
174
- questionToken,
175
- type,
176
- initializer,
177
- }: {
178
- decorators?: Array<ts.Decorator>
179
- modifiers?: Array<ts.Modifier>
180
- dotDotDotToken?: ts.DotDotDotToken
181
- questionToken?: ts.QuestionToken | boolean
182
- type?: ts.TypeNode
183
- initializer?: ts.Expression
184
- },
185
- ): ts.ParameterDeclaration {
186
- return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer)
187
- }
188
-
189
- export function createJSDoc({ comments }: { comments: string[] }) {
190
- if (!comments.length) {
191
- return null
192
- }
193
- return factory.createJSDocComment(
194
- factory.createNodeArray(
195
- comments.map((comment, i) => {
196
- if (i === comments.length - 1) {
197
- return factory.createJSDocText(comment)
198
- }
199
-
200
- return factory.createJSDocText(`${comment}\n`)
201
- }),
202
- ),
203
- )
204
- }
205
-
206
- /**
207
- * @link https://github.com/microsoft/TypeScript/issues/44151
208
- */
209
- export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { node: TNode; comments: Array<string | undefined> }) {
210
- const filteredComments = comments.filter(Boolean)
211
-
212
- if (!filteredComments.length) {
213
- return node
214
- }
215
-
216
- const text = filteredComments.reduce((acc = '', comment = '') => {
217
- return `${acc}\n * ${comment.replaceAll('*/', '*\\/')}`
218
- }, '*')
219
-
220
- // Use the node directly instead of spreading to avoid creating Unknown nodes
221
- // TypeScript's addSyntheticLeadingComment accepts the node as-is
222
- return ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `${text || '*'}\n`, true)
223
- }
224
-
225
- export function createIndexSignature(
226
- type: ts.TypeNode,
227
- {
228
- modifiers,
229
- indexName = 'key',
230
- indexType = factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
231
- }: {
232
- indexName?: string
233
- indexType?: ts.TypeNode
234
- decorators?: Array<ts.Decorator>
235
- modifiers?: Array<ts.Modifier>
236
- } = {},
237
- ) {
238
- return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type)
239
- }
240
-
241
- export function createTypeAliasDeclaration({
242
- modifiers,
243
- name,
244
- typeParameters,
245
- type,
246
- }: {
247
- modifiers?: Array<ts.Modifier>
248
- name: string | ts.Identifier
249
- typeParameters?: Array<ts.TypeParameterDeclaration>
250
- type: ts.TypeNode
251
- }) {
252
- return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type)
253
- }
254
-
255
- export function createInterfaceDeclaration({
256
- modifiers,
257
- name,
258
- typeParameters,
259
- members,
260
- }: {
261
- modifiers?: Array<ts.Modifier>
262
- name: string | ts.Identifier
263
- typeParameters?: Array<ts.TypeParameterDeclaration>
264
- members: Array<ts.TypeElement>
265
- }) {
266
- return factory.createInterfaceDeclaration(modifiers, name, typeParameters, undefined, members)
267
- }
268
-
269
- export function createTypeDeclaration({
270
- syntax,
271
- isExportable,
272
- comments,
273
- name,
274
- type,
275
- }: {
276
- syntax: 'type' | 'interface'
277
- comments: Array<string | undefined>
278
- isExportable?: boolean
279
- name: string | ts.Identifier
280
- type: ts.TypeNode
281
- }) {
282
- if (syntax === 'interface' && 'members' in type) {
283
- const node = createInterfaceDeclaration({
284
- members: type.members as Array<ts.TypeElement>,
285
- modifiers: isExportable ? [modifiers.export] : [],
286
- name,
287
- typeParameters: undefined,
288
- })
289
-
290
- return appendJSDocToNode({
291
- node,
292
- comments,
293
- })
294
- }
295
-
296
- const node = createTypeAliasDeclaration({
297
- type,
298
- modifiers: isExportable ? [modifiers.export] : [],
299
- name,
300
- typeParameters: undefined,
301
- })
302
-
303
- return appendJSDocToNode({
304
- node,
305
- comments,
306
- })
307
- }
308
-
309
- export function createNamespaceDeclaration({ statements, name }: { name: string; statements: ts.Statement[] }) {
310
- return factory.createModuleDeclaration(
311
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
312
- factory.createIdentifier(name),
313
- factory.createModuleBlock(statements),
314
- ts.NodeFlags.Namespace,
315
- )
316
- }
317
-
318
- /**
319
- * In { propertyName: string; name?: string } is `name` being used to make the type more unique when multiple same names are used.
320
- * @example `import { Pet as Cat } from './Pet'`
321
- */
322
- export function createImportDeclaration({
323
- name,
324
- path,
325
- isTypeOnly = false,
326
- isNameSpace = false,
327
- }: {
328
- name: string | Array<string | { propertyName: string; name?: string }>
329
- path: string
330
- isTypeOnly?: boolean
331
- isNameSpace?: boolean
332
- }) {
333
- if (!Array.isArray(name)) {
334
- let importPropertyName: ts.Identifier | undefined = factory.createIdentifier(name)
335
- let importName: ts.NamedImportBindings | undefined
336
-
337
- if (isNameSpace) {
338
- importPropertyName = undefined
339
- importName = factory.createNamespaceImport(factory.createIdentifier(name))
340
- }
341
-
342
- return factory.createImportDeclaration(
343
- undefined,
344
- factory.createImportClause(isTypeOnly, importPropertyName, importName),
345
- factory.createStringLiteral(path),
346
- undefined,
347
- )
348
- }
349
-
350
- // Sort the imports alphabetically for consistent output across platforms
351
- const sortedName = sortBy(name, [(item) => (typeof item === 'object' ? item.propertyName : item), 'asc'])
352
-
353
- return factory.createImportDeclaration(
354
- undefined,
355
- factory.createImportClause(
356
- isTypeOnly,
357
- undefined,
358
- factory.createNamedImports(
359
- sortedName.map((item) => {
360
- if (typeof item === 'object') {
361
- const obj = item as { propertyName: string; name?: string }
362
- if (obj.name) {
363
- return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name))
364
- }
365
-
366
- return factory.createImportSpecifier(false, undefined, factory.createIdentifier(obj.propertyName))
367
- }
368
-
369
- return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))
370
- }),
371
- ),
372
- ),
373
- factory.createStringLiteral(path),
374
- undefined,
375
- )
376
- }
377
-
378
- export function createExportDeclaration({
379
- path,
380
- asAlias,
381
- isTypeOnly = false,
382
- name,
383
- }: {
384
- path: string
385
- asAlias?: boolean
386
- isTypeOnly?: boolean
387
- name?: string | Array<ts.Identifier | string>
388
- }) {
389
- if (name && !Array.isArray(name) && !asAlias) {
390
- console.warn(`When using name as string, asAlias should be true ${name}`)
391
- }
392
-
393
- if (!Array.isArray(name)) {
394
- const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name
395
-
396
- return factory.createExportDeclaration(
397
- undefined,
398
- isTypeOnly,
399
- asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,
400
- factory.createStringLiteral(path),
401
- undefined,
402
- )
403
- }
404
-
405
- // Sort the exports alphabetically for consistent output across platforms
406
- const sortedName = sortBy(name, [(propertyName) => (typeof propertyName === 'string' ? propertyName : propertyName.text), 'asc'])
407
-
408
- return factory.createExportDeclaration(
409
- undefined,
410
- isTypeOnly,
411
- factory.createNamedExports(
412
- sortedName.map((propertyName) => {
413
- return factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName)
414
- }),
415
- ),
416
- factory.createStringLiteral(path),
417
- undefined,
418
- )
419
- }
420
-
421
- /**
422
- * Apply casing transformation to enum keys
423
- */
424
- function applyEnumKeyCasing(key: string, casing: 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none' = 'none'): string {
425
- if (casing === 'none') {
426
- return key
427
- }
428
- if (casing === 'screamingSnakeCase') {
429
- return screamingSnakeCase(key)
430
- }
431
- if (casing === 'snakeCase') {
432
- return snakeCase(key)
433
- }
434
- if (casing === 'pascalCase') {
435
- return pascalCase(key)
436
- }
437
- if (casing === 'camelCase') {
438
- return camelCase(key)
439
- }
440
- return key
441
- }
442
-
443
- export function createEnumDeclaration({
444
- type = 'enum',
445
- name,
446
- typeName,
447
- enums,
448
- enumKeyCasing = 'none',
449
- }: {
450
- /**
451
- * Choose to use `enum`, `asConst`, `asPascalConst`, `constEnum`, or `literal` for enums.
452
- * - `enum`: TypeScript enum
453
- * - `asConst`: const with camelCase name (e.g., `petType`)
454
- * - `asPascalConst`: const with PascalCase name (e.g., `PetType`)
455
- * - `constEnum`: const enum
456
- * - `literal`: literal union type
457
- * @default `'enum'`
458
- */
459
- type?: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral'
460
- /**
461
- * Enum name in camelCase.
462
- */
463
- name: string
464
- /**
465
- * Enum name in PascalCase.
466
- */
467
- typeName: string
468
- enums: [key: string | number, value: string | number | boolean][]
469
- /**
470
- * Choose the casing for enum key names.
471
- * @default 'none'
472
- */
473
- enumKeyCasing?: 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none'
474
- }): [name: ts.Node | undefined, type: ts.Node] {
475
- if (type === 'literal' || type === 'inlineLiteral') {
476
- return [
477
- undefined,
478
- factory.createTypeAliasDeclaration(
479
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
480
- factory.createIdentifier(typeName),
481
- undefined,
482
- factory.createUnionTypeNode(
483
- enums
484
- .map(([_key, value]) => {
485
- if (isNumber(value)) {
486
- if (value < 0) {
487
- return factory.createLiteralTypeNode(
488
- factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value))),
489
- )
490
- }
491
- return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()))
492
- }
493
-
494
- if (typeof value === 'boolean') {
495
- return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse())
496
- }
497
- if (value) {
498
- return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()))
499
- }
500
-
501
- return undefined
502
- })
503
- .filter(Boolean),
504
- ),
505
- ),
506
- ]
507
- }
508
-
509
- if (type === 'enum' || type === 'constEnum') {
510
- return [
511
- undefined,
512
- factory.createEnumDeclaration(
513
- [factory.createToken(ts.SyntaxKind.ExportKeyword), type === 'constEnum' ? factory.createToken(ts.SyntaxKind.ConstKeyword) : undefined].filter(Boolean),
514
- factory.createIdentifier(typeName),
515
- enums
516
- .map(([key, value]) => {
517
- let initializer: ts.Expression = factory.createStringLiteral(value?.toString())
518
- const isExactNumber = Number.parseInt(value.toString(), 10) === value
519
-
520
- if (isExactNumber && isNumber(Number.parseInt(value.toString(), 10))) {
521
- if ((value as number) < 0) {
522
- initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value as number)))
523
- } else {
524
- initializer = factory.createNumericLiteral(value as number)
525
- }
526
- }
527
-
528
- if (typeof value === 'boolean') {
529
- initializer = value ? factory.createTrue() : factory.createFalse()
530
- }
531
-
532
- if (isNumber(Number.parseInt(key.toString(), 10))) {
533
- const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing)
534
- return factory.createEnumMember(propertyName(casingKey), initializer)
535
- }
536
-
537
- if (key) {
538
- const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing)
539
- return factory.createEnumMember(propertyName(casingKey), initializer)
540
- }
541
-
542
- return undefined
543
- })
544
- .filter(Boolean),
545
- ),
546
- ]
547
- }
548
-
549
- // used when using `as const` instead of an TypeScript enum.
550
- // name is already PascalCase for asPascalConst and camelCase for asConst (set in Type.tsx)
551
- // typeName has the Key suffix for type alias, so we use name for the const identifier
552
- const identifierName = name
553
-
554
- // When there are no enum items (empty or all-null enum), don't generate a runtime const.
555
- // Return undefined for nameNode so the barrel won't try to export a non-existent symbol.
556
- // Use `never` as the type alias to keep references valid without creating a broken const.
557
- if (enums.length === 0) {
558
- return [
559
- undefined,
560
- factory.createTypeAliasDeclaration(
561
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
562
- factory.createIdentifier(typeName),
563
- undefined,
564
- factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
565
- ),
566
- ]
567
- }
568
-
569
- return [
570
- factory.createVariableStatement(
571
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
572
- factory.createVariableDeclarationList(
573
- [
574
- factory.createVariableDeclaration(
575
- factory.createIdentifier(identifierName),
576
- undefined,
577
- undefined,
578
- factory.createAsExpression(
579
- factory.createObjectLiteralExpression(
580
- enums
581
- .map(([key, value]) => {
582
- let initializer: ts.Expression = factory.createStringLiteral(value?.toString())
583
-
584
- if (isNumber(value)) {
585
- // Error: Negative numbers should be created in combination with createPrefixUnaryExpression factory.
586
- // The method createNumericLiteral only accepts positive numbers
587
- // or those combined with createPrefixUnaryExpression.
588
- // Therefore, we need to ensure that the number is not negative.
589
- if (value < 0) {
590
- initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)))
591
- } else {
592
- initializer = factory.createNumericLiteral(value)
593
- }
594
- }
595
-
596
- if (typeof value === 'boolean') {
597
- initializer = value ? factory.createTrue() : factory.createFalse()
598
- }
599
-
600
- if (key) {
601
- const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing)
602
- return factory.createPropertyAssignment(propertyName(casingKey), initializer)
603
- }
604
-
605
- return undefined
606
- })
607
- .filter(Boolean),
608
- true,
609
- ),
610
- factory.createTypeReferenceNode(factory.createIdentifier('const'), undefined),
611
- ),
612
- ),
613
- ],
614
- ts.NodeFlags.Const,
615
- ),
616
- ),
617
- factory.createTypeAliasDeclaration(
618
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
619
- factory.createIdentifier(typeName),
620
- undefined,
621
- factory.createIndexedAccessTypeNode(
622
- factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), undefined)),
623
- factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), undefined)),
624
- ),
625
- ),
626
- ]
627
- }
628
-
629
- export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array<string> | string; type: ts.TypeNode; nonNullable?: boolean }) {
630
- const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier('NonNullable'), [type]) : type
631
-
632
- if (Array.isArray(keys)) {
633
- return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [
634
- node,
635
- factory.createUnionTypeNode(
636
- keys.map((key) => {
637
- return factory.createLiteralTypeNode(factory.createStringLiteral(key))
638
- }),
639
- ),
640
- ])
641
- }
642
-
643
- return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))])
644
- }
645
-
646
- export const keywordTypeNodes = {
647
- any: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
648
- unknown: factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
649
- void: factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
650
- number: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
651
- integer: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
652
- bigint: factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword),
653
- object: factory.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword),
654
- string: factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
655
- boolean: factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword),
656
- undefined: factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
657
- null: factory.createLiteralTypeNode(factory.createToken(ts.SyntaxKind.NullKeyword)),
658
- never: factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
659
- } as const
660
-
661
- /**
662
- * Converts a path like '/pet/{petId}/uploadImage' to a template literal type
663
- * like `/pet/${string}/uploadImage`
664
- */
665
- /**
666
- * Converts an OAS-style path (e.g. `/pets/{petId}`) or an Express-style path
667
- * (e.g. `/pets/:petId`) to a TypeScript template literal type
668
- * like `` `/pets/${string}` ``.
669
- */
670
- export function createUrlTemplateType(path: string): ts.TypeNode {
671
- // normalized Express `:param` → OAS `{param}` so a single regex handles both.
672
- const normalized = path.replace(/:([^/]+)/g, '{$1}')
673
-
674
- if (!normalized.includes('{')) {
675
- return factory.createLiteralTypeNode(factory.createStringLiteral(normalized))
676
- }
677
-
678
- const segments = normalized.split(/(\{[^}]+\})/)
679
- const parts: string[] = []
680
- const parameterIndices: number[] = []
681
-
682
- segments.forEach((segment) => {
683
- if (segment.startsWith('{') && segment.endsWith('}')) {
684
- parameterIndices.push(parts.length)
685
- parts.push(segment)
686
- } else if (segment) {
687
- parts.push(segment)
688
- }
689
- })
690
-
691
- const head = ts.factory.createTemplateHead(parts[0] || '')
692
- const templateSpans: ts.TemplateLiteralTypeSpan[] = []
693
-
694
- parameterIndices.forEach((paramIndex, i) => {
695
- const isLast = i === parameterIndices.length - 1
696
- const nextPart = parts[paramIndex + 1] || ''
697
- const literal = isLast ? ts.factory.createTemplateTail(nextPart) : ts.factory.createTemplateMiddle(nextPart)
698
- templateSpans.push(ts.factory.createTemplateLiteralTypeSpan(keywordTypeNodes.string, literal))
699
- })
700
-
701
- return ts.factory.createTemplateLiteralType(head, templateSpans)
702
- }
703
-
704
- export const createTypeLiteralNode = factory.createTypeLiteralNode
705
-
706
- export const createTypeReferenceNode = factory.createTypeReferenceNode
707
- export const createNumericLiteral = factory.createNumericLiteral
708
- export const createStringLiteral = factory.createStringLiteral
709
-
710
- export const createArrayTypeNode = factory.createArrayTypeNode
711
- export const createParenthesizedType = factory.createParenthesizedType
712
-
713
- export const createLiteralTypeNode = factory.createLiteralTypeNode
714
- export const createNull = factory.createNull
715
- export const createIdentifier = factory.createIdentifier
716
-
717
- export const createOptionalTypeNode = factory.createOptionalTypeNode
718
- export const createTupleTypeNode = factory.createTupleTypeNode
719
- export const createRestTypeNode = factory.createRestTypeNode
720
- export const createTrue = factory.createTrue
721
- export const createFalse = factory.createFalse
722
- export const createIndexedAccessTypeNode = factory.createIndexedAccessTypeNode
723
- export const createTypeOperatorNode = factory.createTypeOperatorNode
724
- export const createPrefixUnaryExpression = factory.createPrefixUnaryExpression
725
-
726
- export { SyntaxKind }