@kubb/plugin-ts 5.0.0-alpha.8 → 5.0.0-beta.10
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/LICENSE +17 -10
- package/README.md +26 -7
- package/dist/index.cjs +1526 -4
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +574 -4
- package/dist/index.js +1488 -2
- package/dist/index.js.map +1 -0
- package/extension.yaml +632 -0
- package/package.json +43 -65
- package/src/components/{v2/Enum.tsx → Enum.tsx} +33 -17
- package/src/components/Type.tsx +31 -161
- package/src/constants.ts +15 -5
- package/src/factory.ts +295 -39
- package/src/generators/typeGenerator.tsx +248 -420
- package/src/index.ts +9 -2
- package/src/plugin.ts +67 -205
- package/src/printers/functionPrinter.ts +197 -0
- package/src/printers/printerTs.ts +329 -0
- package/src/resolvers/resolverTs.ts +66 -0
- package/src/types.ts +238 -94
- package/src/utils.ts +129 -0
- package/dist/components-CRu8IKY3.js +0 -729
- package/dist/components-CRu8IKY3.js.map +0 -1
- package/dist/components-DeNDKlzf.cjs +0 -982
- package/dist/components-DeNDKlzf.cjs.map +0 -1
- package/dist/components.cjs +0 -3
- package/dist/components.d.ts +0 -36
- package/dist/components.js +0 -2
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -480
- package/dist/generators.js +0 -2
- package/dist/plugin-D5NGPj0v.js +0 -1232
- package/dist/plugin-D5NGPj0v.js.map +0 -1
- package/dist/plugin-MLTxoa8p.cjs +0 -1279
- package/dist/plugin-MLTxoa8p.cjs.map +0 -1
- package/dist/types-CsvB6X5Y.d.ts +0 -167
- package/src/components/index.ts +0 -1
- package/src/components/v2/Type.tsx +0 -59
- package/src/generators/index.ts +0 -2
- package/src/generators/v2/typeGenerator.tsx +0 -171
- package/src/generators/v2/utils.ts +0 -140
- package/src/parser.ts +0 -389
- package/src/printer.ts +0 -368
package/src/factory.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { camelCase, pascalCase, screamingSnakeCase, snakeCase } from '@internals/utils'
|
|
2
|
+
import { ast } from '@kubb/core'
|
|
2
3
|
import { isNumber, sortBy } from 'remeda'
|
|
3
4
|
import ts from 'typescript'
|
|
5
|
+
import { OPTIONAL_ADDS_UNDEFINED } from './constants.ts'
|
|
4
6
|
|
|
5
7
|
const { SyntaxKind, factory } = ts
|
|
6
8
|
|
|
7
9
|
// https://ts-ast-viewer.com/
|
|
8
10
|
|
|
11
|
+
/**
|
|
12
|
+
* TypeScript AST modifiers for common keywords (async, export, const, static).
|
|
13
|
+
*/
|
|
9
14
|
export const modifiers = {
|
|
10
15
|
async: factory.createModifier(ts.SyntaxKind.AsyncKeyword),
|
|
11
16
|
export: factory.createModifier(ts.SyntaxKind.ExportKeyword),
|
|
@@ -13,22 +18,19 @@ export const modifiers = {
|
|
|
13
18
|
static: factory.createModifier(ts.SyntaxKind.StaticKeyword),
|
|
14
19
|
} as const
|
|
15
20
|
|
|
21
|
+
/**
|
|
22
|
+
* TypeScript syntax kind constants for union, literal, and string types.
|
|
23
|
+
*/
|
|
16
24
|
export const syntaxKind = {
|
|
17
25
|
union: SyntaxKind.UnionType as 192,
|
|
18
26
|
literalType: SyntaxKind.LiteralType,
|
|
19
27
|
stringLiteral: SyntaxKind.StringLiteral,
|
|
20
28
|
} as const
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return keywordTypeNodes.any
|
|
25
|
-
}
|
|
26
|
-
if (unknownType === 'void') {
|
|
27
|
-
return keywordTypeNodes.void
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return keywordTypeNodes.unknown
|
|
30
|
+
function isNonNullable<T>(value: T | null | undefined): value is T {
|
|
31
|
+
return value !== null && value !== undefined
|
|
31
32
|
}
|
|
33
|
+
|
|
32
34
|
function isValidIdentifier(str: string): boolean {
|
|
33
35
|
if (!str.length || str.trim() !== str) {
|
|
34
36
|
return false
|
|
@@ -48,6 +50,10 @@ function propertyName(name: string | ts.PropertyName): ts.PropertyName {
|
|
|
48
50
|
|
|
49
51
|
const questionToken = factory.createToken(ts.SyntaxKind.QuestionToken)
|
|
50
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Creates a question token for optional type annotations.
|
|
55
|
+
* Pass `true` to use the cached token, or provide a pre-created token.
|
|
56
|
+
*/
|
|
51
57
|
export function createQuestionToken(token?: boolean | ts.QuestionToken) {
|
|
52
58
|
if (!token) {
|
|
53
59
|
return undefined
|
|
@@ -58,6 +64,10 @@ export function createQuestionToken(token?: boolean | ts.QuestionToken) {
|
|
|
58
64
|
return token
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Creates a TypeScript intersection type node from multiple type nodes.
|
|
69
|
+
* Returns the single node if only one is provided, or wraps in parentheses if requested.
|
|
70
|
+
*/
|
|
61
71
|
export function createIntersectionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
|
|
62
72
|
if (!nodes.length) {
|
|
63
73
|
return null
|
|
@@ -77,27 +87,15 @@ export function createIntersectionDeclaration({ nodes, withParentheses }: { node
|
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
90
|
+
* Creates a TypeScript array type node.
|
|
91
|
+
* Use `arrayType: 'array'` for bracket syntax (`T[]`), or `'generic'` for `Array<T>`.
|
|
92
|
+
*
|
|
93
|
+
* @example Array bracket syntax
|
|
94
|
+
* `createArrayDeclaration({ nodes: [stringType], arrayType: 'array' }) // → string[]`
|
|
95
|
+
*
|
|
96
|
+
* @example Generic Array syntax
|
|
97
|
+
* `createArrayDeclaration({ nodes: [stringType], arrayType: 'generic' }) // → Array<string>`
|
|
82
98
|
*/
|
|
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
99
|
export function createArrayDeclaration({ nodes, arrayType = 'array' }: { nodes: Array<ts.TypeNode>; arrayType?: 'array' | 'generic' }): ts.TypeNode | null {
|
|
102
100
|
if (!nodes.length) {
|
|
103
101
|
return factory.createTupleTypeNode([])
|
|
@@ -125,7 +123,8 @@ export function createArrayDeclaration({ nodes, arrayType = 'array' }: { nodes:
|
|
|
125
123
|
|
|
126
124
|
/**
|
|
127
125
|
* Minimum nodes length of 2
|
|
128
|
-
* @example
|
|
126
|
+
* @example Union type example
|
|
127
|
+
* `string | number`
|
|
129
128
|
*/
|
|
130
129
|
export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode {
|
|
131
130
|
if (!nodes.length) {
|
|
@@ -145,6 +144,10 @@ export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Arra
|
|
|
145
144
|
return node
|
|
146
145
|
}
|
|
147
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Creates a TypeScript property signature for object/interface members.
|
|
149
|
+
* Supports optional markers, readonly modifiers, and type annotations.
|
|
150
|
+
*/
|
|
148
151
|
export function createPropertySignature({
|
|
149
152
|
readOnly,
|
|
150
153
|
modifiers = [],
|
|
@@ -159,13 +162,18 @@ export function createPropertySignature({
|
|
|
159
162
|
type?: ts.TypeNode
|
|
160
163
|
}) {
|
|
161
164
|
return factory.createPropertySignature(
|
|
162
|
-
[...modifiers, readOnly ? factory.createToken(ts.SyntaxKind.ReadonlyKeyword) : undefined].filter(
|
|
165
|
+
[...modifiers, readOnly ? factory.createToken(ts.SyntaxKind.ReadonlyKeyword) : undefined].filter(
|
|
166
|
+
(modifier): modifier is ts.Modifier => modifier !== undefined,
|
|
167
|
+
),
|
|
163
168
|
propertyName(name),
|
|
164
169
|
createQuestionToken(questionToken),
|
|
165
170
|
type,
|
|
166
171
|
)
|
|
167
172
|
}
|
|
168
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Creates a function parameter declaration with optional markers, rest parameters, and type annotations.
|
|
176
|
+
*/
|
|
169
177
|
export function createParameterSignature(
|
|
170
178
|
name: string | ts.BindingName,
|
|
171
179
|
{
|
|
@@ -186,6 +194,10 @@ export function createParameterSignature(
|
|
|
186
194
|
return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer)
|
|
187
195
|
}
|
|
188
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Creates a JSDoc comment node from an array of comment strings.
|
|
199
|
+
* Returns null if no comments are provided.
|
|
200
|
+
*/
|
|
189
201
|
export function createJSDoc({ comments }: { comments: string[] }) {
|
|
190
202
|
if (!comments.length) {
|
|
191
203
|
return null
|
|
@@ -204,7 +216,10 @@ export function createJSDoc({ comments }: { comments: string[] }) {
|
|
|
204
216
|
}
|
|
205
217
|
|
|
206
218
|
/**
|
|
207
|
-
*
|
|
219
|
+
* Attaches JSDoc comments to an AST node as synthetic leading comments.
|
|
220
|
+
* Filters out undefined comments before attaching.
|
|
221
|
+
*
|
|
222
|
+
* @see https://github.com/microsoft/TypeScript/issues/44151
|
|
208
223
|
*/
|
|
209
224
|
export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { node: TNode; comments: Array<string | undefined> }) {
|
|
210
225
|
const filteredComments = comments.filter(Boolean)
|
|
@@ -222,6 +237,10 @@ export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { n
|
|
|
222
237
|
return ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `${text || '*'}\n`, true)
|
|
223
238
|
}
|
|
224
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Creates a TypeScript index signature for dynamic property access.
|
|
242
|
+
* Defines the key type (default: `string`) and value type on an object.
|
|
243
|
+
*/
|
|
225
244
|
export function createIndexSignature(
|
|
226
245
|
type: ts.TypeNode,
|
|
227
246
|
{
|
|
@@ -238,6 +257,9 @@ export function createIndexSignature(
|
|
|
238
257
|
return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type)
|
|
239
258
|
}
|
|
240
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Creates a TypeScript type alias declaration with optional modifiers and type parameters.
|
|
262
|
+
*/
|
|
241
263
|
export function createTypeAliasDeclaration({
|
|
242
264
|
modifiers,
|
|
243
265
|
name,
|
|
@@ -252,6 +274,9 @@ export function createTypeAliasDeclaration({
|
|
|
252
274
|
return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type)
|
|
253
275
|
}
|
|
254
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Creates a TypeScript interface declaration with optional modifiers, type parameters, and members.
|
|
279
|
+
*/
|
|
255
280
|
export function createInterfaceDeclaration({
|
|
256
281
|
modifiers,
|
|
257
282
|
name,
|
|
@@ -266,6 +291,10 @@ export function createInterfaceDeclaration({
|
|
|
266
291
|
return factory.createInterfaceDeclaration(modifiers, name, typeParameters, undefined, members)
|
|
267
292
|
}
|
|
268
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Creates a TypeScript type declaration as either a type alias or interface.
|
|
296
|
+
* Intelligently selects the syntax based on the type structure and attaches JSDoc comments.
|
|
297
|
+
*/
|
|
269
298
|
export function createTypeDeclaration({
|
|
270
299
|
syntax,
|
|
271
300
|
isExportable,
|
|
@@ -281,7 +310,7 @@ export function createTypeDeclaration({
|
|
|
281
310
|
}) {
|
|
282
311
|
if (syntax === 'interface' && 'members' in type) {
|
|
283
312
|
const node = createInterfaceDeclaration({
|
|
284
|
-
members: type
|
|
313
|
+
members: [...(type as ts.TypeLiteralNode).members],
|
|
285
314
|
modifiers: isExportable ? [modifiers.export] : [],
|
|
286
315
|
name,
|
|
287
316
|
typeParameters: undefined,
|
|
@@ -306,6 +335,9 @@ export function createTypeDeclaration({
|
|
|
306
335
|
})
|
|
307
336
|
}
|
|
308
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Creates a TypeScript namespace declaration (exported module).
|
|
340
|
+
*/
|
|
309
341
|
export function createNamespaceDeclaration({ statements, name }: { name: string; statements: ts.Statement[] }) {
|
|
310
342
|
return factory.createModuleDeclaration(
|
|
311
343
|
[factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
@@ -316,8 +348,17 @@ export function createNamespaceDeclaration({ statements, name }: { name: string;
|
|
|
316
348
|
}
|
|
317
349
|
|
|
318
350
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
351
|
+
* Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
|
|
352
|
+
* Optionally rename imported members with `propertyName` and `name` pairs.
|
|
353
|
+
*
|
|
354
|
+
* @example Default import
|
|
355
|
+
* `import Pet from './Pet'`
|
|
356
|
+
*
|
|
357
|
+
* @example Named imports with rename
|
|
358
|
+
* `import { Pet as Cat } from './Pet'`
|
|
359
|
+
*
|
|
360
|
+
* @example Namespace import
|
|
361
|
+
* `import * as Pet from './Pet'`
|
|
321
362
|
*/
|
|
322
363
|
export function createImportDeclaration({
|
|
323
364
|
name,
|
|
@@ -375,6 +416,10 @@ export function createImportDeclaration({
|
|
|
375
416
|
)
|
|
376
417
|
}
|
|
377
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Creates an export declaration with support for named exports, namespace exports, and type-only exports.
|
|
421
|
+
* Sorts export names alphabetically for consistent output across platforms.
|
|
422
|
+
*/
|
|
378
423
|
export function createExportDeclaration({
|
|
379
424
|
path,
|
|
380
425
|
asAlias,
|
|
@@ -440,6 +485,20 @@ function applyEnumKeyCasing(key: string, casing: 'screamingSnakeCase' | 'snakeCa
|
|
|
440
485
|
return key
|
|
441
486
|
}
|
|
442
487
|
|
|
488
|
+
/**
|
|
489
|
+
* Creates a TypeScript enum declaration or equivalent construct in various formats.
|
|
490
|
+
* Returns a tuple of [name node, type node] - name node may be undefined for certain types.
|
|
491
|
+
*
|
|
492
|
+
* @example
|
|
493
|
+
* ```ts
|
|
494
|
+
* const [name, type] = createEnumDeclaration({
|
|
495
|
+
* type: 'enum',
|
|
496
|
+
* name: 'petType',
|
|
497
|
+
* typeName: 'PetType',
|
|
498
|
+
* enums: [['cat', 'cat'], ['dog', 'dog']],
|
|
499
|
+
* })
|
|
500
|
+
* ```
|
|
501
|
+
*/
|
|
443
502
|
export function createEnumDeclaration({
|
|
444
503
|
type = 'enum',
|
|
445
504
|
name,
|
|
@@ -500,7 +559,7 @@ export function createEnumDeclaration({
|
|
|
500
559
|
|
|
501
560
|
return undefined
|
|
502
561
|
})
|
|
503
|
-
.filter(
|
|
562
|
+
.filter((node): node is ts.LiteralTypeNode => node !== undefined),
|
|
504
563
|
),
|
|
505
564
|
),
|
|
506
565
|
]
|
|
@@ -510,7 +569,9 @@ export function createEnumDeclaration({
|
|
|
510
569
|
return [
|
|
511
570
|
undefined,
|
|
512
571
|
factory.createEnumDeclaration(
|
|
513
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword), type === 'constEnum' ? factory.createToken(ts.SyntaxKind.ConstKeyword) : undefined].filter(
|
|
572
|
+
[factory.createToken(ts.SyntaxKind.ExportKeyword), type === 'constEnum' ? factory.createToken(ts.SyntaxKind.ConstKeyword) : undefined].filter(
|
|
573
|
+
(modifier): modifier is ts.ModifierToken<ts.SyntaxKind.ExportKeyword> | ts.ModifierToken<ts.SyntaxKind.ConstKeyword> => modifier !== undefined,
|
|
574
|
+
),
|
|
514
575
|
factory.createIdentifier(typeName),
|
|
515
576
|
enums
|
|
516
577
|
.map(([key, value]) => {
|
|
@@ -541,7 +602,7 @@ export function createEnumDeclaration({
|
|
|
541
602
|
|
|
542
603
|
return undefined
|
|
543
604
|
})
|
|
544
|
-
.filter(
|
|
605
|
+
.filter((member): member is ts.EnumMember => member !== undefined),
|
|
545
606
|
),
|
|
546
607
|
]
|
|
547
608
|
}
|
|
@@ -604,7 +665,7 @@ export function createEnumDeclaration({
|
|
|
604
665
|
|
|
605
666
|
return undefined
|
|
606
667
|
})
|
|
607
|
-
.filter(
|
|
668
|
+
.filter((property): property is ts.PropertyAssignment => property !== undefined),
|
|
608
669
|
true,
|
|
609
670
|
),
|
|
610
671
|
factory.createTypeReferenceNode(factory.createIdentifier('const'), undefined),
|
|
@@ -626,6 +687,10 @@ export function createEnumDeclaration({
|
|
|
626
687
|
]
|
|
627
688
|
}
|
|
628
689
|
|
|
690
|
+
/**
|
|
691
|
+
* Creates a TypeScript `Omit<T, Keys>` type reference node.
|
|
692
|
+
* Optionally wraps the type in `NonNullable<T>` if `nonNullable` is true.
|
|
693
|
+
*/
|
|
629
694
|
export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array<string> | string; type: ts.TypeNode; nonNullable?: boolean }) {
|
|
630
695
|
const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier('NonNullable'), [type]) : type
|
|
631
696
|
|
|
@@ -643,6 +708,10 @@ export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array
|
|
|
643
708
|
return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))])
|
|
644
709
|
}
|
|
645
710
|
|
|
711
|
+
/**
|
|
712
|
+
* Pre-built TypeScript keyword type nodes for common primitive types.
|
|
713
|
+
* Use these to avoid repeatedly creating the same type nodes.
|
|
714
|
+
*/
|
|
646
715
|
export const keywordTypeNodes = {
|
|
647
716
|
any: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
|
|
648
717
|
unknown: factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
|
@@ -701,26 +770,213 @@ export function createUrlTemplateType(path: string): ts.TypeNode {
|
|
|
701
770
|
return ts.factory.createTemplateLiteralType(head, templateSpans)
|
|
702
771
|
}
|
|
703
772
|
|
|
773
|
+
/**
|
|
774
|
+
* Creates a TypeScript type literal node (anonymous object type).
|
|
775
|
+
*/
|
|
704
776
|
export const createTypeLiteralNode = factory.createTypeLiteralNode
|
|
705
777
|
|
|
778
|
+
/**
|
|
779
|
+
* Creates a TypeScript type reference node (e.g., `Array<string>`, `Record<K, V>`).
|
|
780
|
+
*/
|
|
706
781
|
export const createTypeReferenceNode = factory.createTypeReferenceNode
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Creates a numeric literal type node.
|
|
785
|
+
*/
|
|
707
786
|
export const createNumericLiteral = factory.createNumericLiteral
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Creates a string literal type node.
|
|
790
|
+
*/
|
|
708
791
|
export const createStringLiteral = factory.createStringLiteral
|
|
709
792
|
|
|
793
|
+
/**
|
|
794
|
+
* Creates an array type node (e.g., `T[]`).
|
|
795
|
+
*/
|
|
710
796
|
export const createArrayTypeNode = factory.createArrayTypeNode
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Creates a parenthesized type node to control operator precedence.
|
|
800
|
+
*/
|
|
711
801
|
export const createParenthesizedType = factory.createParenthesizedType
|
|
712
802
|
|
|
803
|
+
/**
|
|
804
|
+
* Creates a literal type node (e.g., `'hello'`, `42`, `true`).
|
|
805
|
+
*/
|
|
713
806
|
export const createLiteralTypeNode = factory.createLiteralTypeNode
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Creates a null literal type node.
|
|
810
|
+
*/
|
|
714
811
|
export const createNull = factory.createNull
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Creates an identifier node.
|
|
815
|
+
*/
|
|
715
816
|
export const createIdentifier = factory.createIdentifier
|
|
716
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Creates an optional type node (e.g., `T | undefined`).
|
|
820
|
+
*/
|
|
717
821
|
export const createOptionalTypeNode = factory.createOptionalTypeNode
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Creates a tuple type node (e.g., `[string, number]`).
|
|
825
|
+
*/
|
|
718
826
|
export const createTupleTypeNode = factory.createTupleTypeNode
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Creates a rest type node for variadic tuple elements (e.g., `...T[]`).
|
|
830
|
+
*/
|
|
719
831
|
export const createRestTypeNode = factory.createRestTypeNode
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Creates a boolean true literal type node.
|
|
835
|
+
*/
|
|
720
836
|
export const createTrue = factory.createTrue
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Creates a boolean false literal type node.
|
|
840
|
+
*/
|
|
721
841
|
export const createFalse = factory.createFalse
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Creates an indexed access type node (e.g., `T[K]`).
|
|
845
|
+
*/
|
|
722
846
|
export const createIndexedAccessTypeNode = factory.createIndexedAccessTypeNode
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Creates a type operator node (e.g., `keyof T`, `readonly T[]`).
|
|
850
|
+
*/
|
|
723
851
|
export const createTypeOperatorNode = factory.createTypeOperatorNode
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Creates a prefix unary expression (e.g., negative numbers, logical not).
|
|
855
|
+
*/
|
|
724
856
|
export const createPrefixUnaryExpression = factory.createPrefixUnaryExpression
|
|
725
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Exports TypeScript SyntaxKind enum for AST node type checking.
|
|
860
|
+
*/
|
|
726
861
|
export { SyntaxKind }
|
|
862
|
+
|
|
863
|
+
// ─── Printer helpers ──────────────────────────────────────────────────────────
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Converts a primitive const value to a TypeScript literal type node.
|
|
867
|
+
* Handles negative numbers via a prefix unary expression.
|
|
868
|
+
*/
|
|
869
|
+
export function constToTypeNode(value: string | number | boolean, format: 'string' | 'number' | 'boolean'): ts.TypeNode | undefined {
|
|
870
|
+
if (format === 'boolean') {
|
|
871
|
+
return createLiteralTypeNode(value === true ? createTrue() : createFalse())
|
|
872
|
+
}
|
|
873
|
+
if (format === 'number' && typeof value === 'number') {
|
|
874
|
+
if (value < 0) {
|
|
875
|
+
return createLiteralTypeNode(createPrefixUnaryExpression(SyntaxKind.MinusToken, createNumericLiteral(Math.abs(value))))
|
|
876
|
+
}
|
|
877
|
+
return createLiteralTypeNode(createNumericLiteral(value))
|
|
878
|
+
}
|
|
879
|
+
return createLiteralTypeNode(createStringLiteral(String(value)))
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
|
|
884
|
+
*/
|
|
885
|
+
export function dateOrStringNode(node: { representation?: string }): ts.TypeNode {
|
|
886
|
+
return node.representation === 'date' ? createTypeReferenceNode(createIdentifier('Date')) : keywordTypeNodes.string
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
|
|
891
|
+
*/
|
|
892
|
+
export function buildMemberNodes(
|
|
893
|
+
members: Array<ast.SchemaNode> | undefined,
|
|
894
|
+
print: (node: ast.SchemaNode) => ts.TypeNode | null | undefined,
|
|
895
|
+
): Array<ts.TypeNode> {
|
|
896
|
+
return (members ?? []).map(print).filter(isNonNullable)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Builds a TypeScript tuple type node from an array schema's `items`,
|
|
901
|
+
* applying min/max slice and optional/rest element rules.
|
|
902
|
+
*/
|
|
903
|
+
export function buildTupleNode(node: ast.ArraySchemaNode, print: (node: ast.SchemaNode) => ts.TypeNode | null | undefined): ts.TypeNode | undefined {
|
|
904
|
+
let items = (node.items ?? []).map(print).filter(isNonNullable)
|
|
905
|
+
|
|
906
|
+
const restNode = node.rest ? (print(node.rest) ?? undefined) : undefined
|
|
907
|
+
const { min, max } = node
|
|
908
|
+
|
|
909
|
+
if (max !== undefined) {
|
|
910
|
+
items = items.slice(0, max)
|
|
911
|
+
if (items.length < max && restNode) {
|
|
912
|
+
items = [...items, ...Array(max - items.length).fill(restNode)]
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (min !== undefined) {
|
|
917
|
+
items = items.map((item, i) => (i >= min ? createOptionalTypeNode(item) : item))
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (max === undefined && restNode) {
|
|
921
|
+
items.push(createRestTypeNode(createArrayTypeNode(restNode)))
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
return createTupleTypeNode(items)
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
|
|
929
|
+
*/
|
|
930
|
+
export function buildPropertyType(
|
|
931
|
+
schema: ast.SchemaNode,
|
|
932
|
+
baseType: ts.TypeNode,
|
|
933
|
+
optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined',
|
|
934
|
+
): ts.TypeNode {
|
|
935
|
+
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType)
|
|
936
|
+
const meta = ast.syncSchemaRef(schema)
|
|
937
|
+
|
|
938
|
+
let type = baseType
|
|
939
|
+
|
|
940
|
+
if (meta.nullable) {
|
|
941
|
+
type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] })
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if ((meta.nullish || meta.optional) && addsUndefined) {
|
|
945
|
+
type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] })
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return type
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
|
|
953
|
+
*/
|
|
954
|
+
export function buildIndexSignatures(
|
|
955
|
+
node: { additionalProperties?: ast.SchemaNode | boolean; patternProperties?: Record<string, ast.SchemaNode> },
|
|
956
|
+
propertyCount: number,
|
|
957
|
+
print: (node: ast.SchemaNode) => ts.TypeNode | null | undefined,
|
|
958
|
+
): Array<ts.TypeElement> {
|
|
959
|
+
const elements: Array<ts.TypeElement> = []
|
|
960
|
+
|
|
961
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
962
|
+
const additionalType = print(node.additionalProperties) ?? keywordTypeNodes.unknown
|
|
963
|
+
|
|
964
|
+
elements.push(createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : additionalType))
|
|
965
|
+
} else if (node.additionalProperties === true) {
|
|
966
|
+
elements.push(createIndexSignature(keywordTypeNodes.unknown))
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if (node.patternProperties) {
|
|
970
|
+
const first = Object.values(node.patternProperties)[0]
|
|
971
|
+
if (first) {
|
|
972
|
+
let patternType = print(first) ?? keywordTypeNodes.unknown
|
|
973
|
+
|
|
974
|
+
if (first.nullable) {
|
|
975
|
+
patternType = createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] })
|
|
976
|
+
}
|
|
977
|
+
elements.push(createIndexSignature(patternType))
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
return elements
|
|
982
|
+
}
|