@kubb/parser-ts 4.1.4 → 5.0.0-alpha.32

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/src/factory.ts DELETED
@@ -1,578 +0,0 @@
1
- import { isNumber } from 'remeda'
2
- import ts from 'typescript'
3
-
4
- const { SyntaxKind, factory } = ts
5
-
6
- // https://ts-ast-viewer.com/
7
-
8
- export const modifiers = {
9
- async: factory.createModifier(ts.SyntaxKind.AsyncKeyword),
10
- export: factory.createModifier(ts.SyntaxKind.ExportKeyword),
11
- const: factory.createModifier(ts.SyntaxKind.ConstKeyword),
12
- static: factory.createModifier(ts.SyntaxKind.StaticKeyword),
13
- } as const
14
-
15
- export const syntaxKind = {
16
- union: SyntaxKind.UnionType as 192,
17
- } as const
18
-
19
- function isValidIdentifier(str: string): boolean {
20
- if (!str.length || str.trim() !== str) {
21
- return false
22
- }
23
- const node = ts.parseIsolatedEntityName(str, ts.ScriptTarget.Latest)
24
-
25
- return !!node && node.kind === ts.SyntaxKind.Identifier && ts.identifierToKeywordKind(node.kind as unknown as ts.Identifier) === undefined
26
- }
27
-
28
- function propertyName(name: string | ts.PropertyName): ts.PropertyName {
29
- if (typeof name === 'string') {
30
- return isValidIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name)
31
- }
32
- return name
33
- }
34
-
35
- const questionToken = factory.createToken(ts.SyntaxKind.QuestionToken)
36
-
37
- export function createQuestionToken(token?: boolean | ts.QuestionToken) {
38
- if (!token) {
39
- return undefined
40
- }
41
- if (token === true) {
42
- return questionToken
43
- }
44
- return token
45
- }
46
-
47
- export function createIntersectionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
48
- if (!nodes.length) {
49
- return null
50
- }
51
-
52
- if (nodes.length === 1) {
53
- return nodes[0] || null
54
- }
55
-
56
- const node = factory.createIntersectionTypeNode(nodes)
57
-
58
- if (withParentheses) {
59
- return factory.createParenthesizedType(node)
60
- }
61
-
62
- return node
63
- }
64
-
65
- /**
66
- * Minimum nodes length of 2
67
- * @example `string & number`
68
- */
69
- export function createTupleDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
70
- if (!nodes.length) {
71
- return null
72
- }
73
-
74
- if (nodes.length === 1) {
75
- return nodes[0] || null
76
- }
77
-
78
- const node = factory.createTupleTypeNode(nodes)
79
-
80
- if (withParentheses) {
81
- return factory.createParenthesizedType(node)
82
- }
83
-
84
- return node
85
- }
86
-
87
- export function createArrayDeclaration({ nodes }: { nodes: Array<ts.TypeNode> }): ts.TypeNode | null {
88
- if (!nodes.length) {
89
- return factory.createTupleTypeNode([])
90
- }
91
-
92
- if (nodes.length === 1) {
93
- return factory.createArrayTypeNode(nodes.at(0)!)
94
- }
95
-
96
- return factory.createExpressionWithTypeArguments(factory.createIdentifier('Array'), [factory.createUnionTypeNode(nodes)])
97
- }
98
-
99
- /**
100
- * Minimum nodes length of 2
101
- * @example `string | number`
102
- */
103
- export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode {
104
- if (!nodes.length) {
105
- return keywordTypeNodes.any
106
- }
107
-
108
- if (nodes.length === 1) {
109
- return nodes[0] as ts.TypeNode
110
- }
111
-
112
- const node = factory.createUnionTypeNode(nodes)
113
-
114
- if (withParentheses) {
115
- return factory.createParenthesizedType(node)
116
- }
117
-
118
- return node
119
- }
120
-
121
- export function createPropertySignature({
122
- readOnly,
123
- modifiers = [],
124
- name,
125
- questionToken,
126
- type,
127
- }: {
128
- readOnly?: boolean
129
- modifiers?: Array<ts.Modifier>
130
- name: ts.PropertyName | string
131
- questionToken?: ts.QuestionToken | boolean
132
- type?: ts.TypeNode
133
- }) {
134
- return factory.createPropertySignature(
135
- [...modifiers, readOnly ? factory.createToken(ts.SyntaxKind.ReadonlyKeyword) : undefined].filter(Boolean),
136
- propertyName(name),
137
- createQuestionToken(questionToken),
138
- type,
139
- )
140
- }
141
-
142
- export function createParameterSignature(
143
- name: string | ts.BindingName,
144
- {
145
- modifiers,
146
- dotDotDotToken,
147
- questionToken,
148
- type,
149
- initializer,
150
- }: {
151
- decorators?: Array<ts.Decorator>
152
- modifiers?: Array<ts.Modifier>
153
- dotDotDotToken?: ts.DotDotDotToken
154
- questionToken?: ts.QuestionToken | boolean
155
- type?: ts.TypeNode
156
- initializer?: ts.Expression
157
- },
158
- ): ts.ParameterDeclaration {
159
- return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer)
160
- }
161
-
162
- export function createJSDoc({ comments }: { comments: string[] }) {
163
- if (!comments.length) {
164
- return null
165
- }
166
- return factory.createJSDocComment(
167
- factory.createNodeArray(
168
- comments.map((comment, i) => {
169
- if (i === comments.length - 1) {
170
- return factory.createJSDocText(comment)
171
- }
172
-
173
- return factory.createJSDocText(`${comment}\n`)
174
- }),
175
- ),
176
- )
177
- }
178
-
179
- /**
180
- * @link https://github.com/microsoft/TypeScript/issues/44151
181
- */
182
- export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { node: TNode; comments: Array<string | undefined> }) {
183
- const filteredComments = comments.filter(Boolean)
184
-
185
- if (!filteredComments.length) {
186
- return node
187
- }
188
-
189
- const text = filteredComments.reduce((acc = '', comment = '') => {
190
- return `${acc}\n * ${comment.replaceAll('*/', '*\\/')}`
191
- }, '*')
192
-
193
- // node: {...node}, with that ts.addSyntheticLeadingComment is appending
194
- return ts.addSyntheticLeadingComment({ ...node }, ts.SyntaxKind.MultiLineCommentTrivia, `${text || '*'}\n`, true)
195
- }
196
-
197
- export function createIndexSignature(
198
- type: ts.TypeNode,
199
- {
200
- modifiers,
201
- indexName = 'key',
202
- indexType = factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
203
- }: {
204
- indexName?: string
205
- indexType?: ts.TypeNode
206
- decorators?: Array<ts.Decorator>
207
- modifiers?: Array<ts.Modifier>
208
- } = {},
209
- ) {
210
- return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type)
211
- }
212
-
213
- export function createTypeAliasDeclaration({
214
- modifiers,
215
- name,
216
- typeParameters,
217
- type,
218
- }: {
219
- modifiers?: Array<ts.Modifier>
220
- name: string | ts.Identifier
221
- typeParameters?: Array<ts.TypeParameterDeclaration>
222
- type: ts.TypeNode
223
- }) {
224
- return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type)
225
- }
226
-
227
- export function createInterfaceDeclaration({
228
- modifiers,
229
- name,
230
- typeParameters,
231
- members,
232
- }: {
233
- modifiers?: Array<ts.Modifier>
234
- name: string | ts.Identifier
235
- typeParameters?: Array<ts.TypeParameterDeclaration>
236
- members: Array<ts.TypeElement>
237
- }) {
238
- return factory.createInterfaceDeclaration(modifiers, name, typeParameters, undefined, members)
239
- }
240
-
241
- export function createTypeDeclaration({
242
- syntax,
243
- isExportable,
244
- comments,
245
- name,
246
- type,
247
- }: {
248
- syntax: 'type' | 'interface'
249
- comments: Array<string | undefined>
250
- isExportable?: boolean
251
- name: string | ts.Identifier
252
- type: ts.TypeNode
253
- }) {
254
- if (syntax === 'interface' && 'members' in type) {
255
- const node = createInterfaceDeclaration({
256
- members: type.members as Array<ts.TypeElement>,
257
- modifiers: isExportable ? [modifiers.export] : [],
258
- name,
259
- typeParameters: undefined,
260
- })
261
-
262
- return appendJSDocToNode({
263
- node,
264
- comments,
265
- })
266
- }
267
-
268
- const node = createTypeAliasDeclaration({
269
- type,
270
- modifiers: isExportable ? [modifiers.export] : [],
271
- name,
272
- typeParameters: undefined,
273
- })
274
-
275
- return appendJSDocToNode({
276
- node,
277
- comments,
278
- })
279
- }
280
-
281
- export function createNamespaceDeclaration({ statements, name }: { name: string; statements: ts.Statement[] }) {
282
- return factory.createModuleDeclaration(
283
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
284
- factory.createIdentifier(name),
285
- factory.createModuleBlock(statements),
286
- ts.NodeFlags.Namespace,
287
- )
288
- }
289
-
290
- /**
291
- * In { propertyName: string; name?: string } is `name` being used to make the type more unique when multiple same names are used.
292
- * @example `import { Pet as Cat } from './Pet'`
293
- */
294
- export function createImportDeclaration({
295
- name,
296
- path,
297
- isTypeOnly = false,
298
- isNameSpace = false,
299
- }: {
300
- name: string | Array<string | { propertyName: string; name?: string }>
301
- path: string
302
- isTypeOnly?: boolean
303
- isNameSpace?: boolean
304
- }) {
305
- if (!Array.isArray(name)) {
306
- let importPropertyName: ts.Identifier | undefined = factory.createIdentifier(name)
307
- let importName: ts.NamedImportBindings | undefined
308
-
309
- if (isNameSpace) {
310
- importPropertyName = undefined
311
- importName = factory.createNamespaceImport(factory.createIdentifier(name))
312
- }
313
-
314
- return factory.createImportDeclaration(
315
- undefined,
316
- factory.createImportClause(isTypeOnly, importPropertyName, importName),
317
- factory.createStringLiteral(path),
318
- undefined,
319
- )
320
- }
321
-
322
- return factory.createImportDeclaration(
323
- undefined,
324
- factory.createImportClause(
325
- isTypeOnly,
326
- undefined,
327
- factory.createNamedImports(
328
- name.map((item) => {
329
- if (typeof item === 'object') {
330
- const obj = item as { propertyName: string; name?: string }
331
- if (obj.name) {
332
- return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name))
333
- }
334
-
335
- return factory.createImportSpecifier(false, undefined, factory.createIdentifier(obj.propertyName))
336
- }
337
-
338
- return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))
339
- }),
340
- ),
341
- ),
342
- factory.createStringLiteral(path),
343
- undefined,
344
- )
345
- }
346
-
347
- export function createExportDeclaration({
348
- path,
349
- asAlias,
350
- isTypeOnly = false,
351
- name,
352
- }: {
353
- path: string
354
- asAlias?: boolean
355
- isTypeOnly?: boolean
356
- name?: string | Array<ts.Identifier | string>
357
- }) {
358
- if (name && !Array.isArray(name) && !asAlias) {
359
- console.warn(`When using name as string, asAlias should be true ${name}`)
360
- }
361
-
362
- if (!Array.isArray(name)) {
363
- const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name
364
-
365
- return factory.createExportDeclaration(
366
- undefined,
367
- isTypeOnly,
368
- asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,
369
- factory.createStringLiteral(path),
370
- undefined,
371
- )
372
- }
373
-
374
- return factory.createExportDeclaration(
375
- undefined,
376
- isTypeOnly,
377
- factory.createNamedExports(
378
- name.map((propertyName) => {
379
- return factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName)
380
- }),
381
- ),
382
- factory.createStringLiteral(path),
383
- undefined,
384
- )
385
- }
386
-
387
- export function createEnumDeclaration({
388
- type = 'enum',
389
- name,
390
- typeName,
391
- enums,
392
- }: {
393
- /**
394
- * @default `'enum'`
395
- */
396
- type?: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal'
397
- /**
398
- * Enum name in camelCase.
399
- */
400
- name: string
401
- /**
402
- * Enum name in PascalCase.
403
- */
404
- typeName: string
405
- enums: [key: string | number, value: string | number | boolean][]
406
- }): [name: ts.Node | undefined, type: ts.Node] {
407
- if (type === 'literal') {
408
- return [
409
- undefined,
410
- factory.createTypeAliasDeclaration(
411
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
412
- factory.createIdentifier(typeName),
413
- undefined,
414
- factory.createUnionTypeNode(
415
- enums
416
- .map(([_key, value]) => {
417
- if (isNumber(value)) {
418
- return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()))
419
- }
420
-
421
- if (typeof value === 'boolean') {
422
- return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse())
423
- }
424
- if (value) {
425
- return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()))
426
- }
427
-
428
- return undefined
429
- })
430
- .filter(Boolean),
431
- ),
432
- ),
433
- ]
434
- }
435
-
436
- if (type === 'enum' || type === 'constEnum') {
437
- return [
438
- undefined,
439
- factory.createEnumDeclaration(
440
- [factory.createToken(ts.SyntaxKind.ExportKeyword), type === 'constEnum' ? factory.createToken(ts.SyntaxKind.ConstKeyword) : undefined].filter(Boolean),
441
- factory.createIdentifier(typeName),
442
- enums
443
- .map(([key, value]) => {
444
- let initializer: ts.Expression = factory.createStringLiteral(value?.toString())
445
- const isExactNumber = Number.parseInt(value.toString(), 10) === value
446
-
447
- if (isExactNumber && isNumber(Number.parseInt(value.toString(), 10))) {
448
- initializer = factory.createNumericLiteral(value as number)
449
- }
450
-
451
- if (typeof value === 'boolean') {
452
- initializer = value ? factory.createTrue() : factory.createFalse()
453
- }
454
-
455
- if (isNumber(Number.parseInt(key.toString(), 10))) {
456
- return factory.createEnumMember(factory.createStringLiteral(`${typeName}_${key}`), initializer)
457
- }
458
-
459
- if (key) {
460
- return factory.createEnumMember(factory.createStringLiteral(`${key}`), initializer)
461
- }
462
-
463
- return undefined
464
- })
465
- .filter(Boolean),
466
- ),
467
- ]
468
- }
469
-
470
- // used when using `as const` instead of an TypeScript enum.
471
- const identifierName = type === 'asPascalConst' ? typeName : name
472
-
473
- return [
474
- factory.createVariableStatement(
475
- [factory.createToken(ts.SyntaxKind.ExportKeyword)],
476
- factory.createVariableDeclarationList(
477
- [
478
- factory.createVariableDeclaration(
479
- factory.createIdentifier(identifierName),
480
- undefined,
481
- undefined,
482
- factory.createAsExpression(
483
- factory.createObjectLiteralExpression(
484
- enums
485
- .map(([key, value]) => {
486
- let initializer: ts.Expression = factory.createStringLiteral(value?.toString())
487
-
488
- if (isNumber(value)) {
489
- // Error: Negative numbers should be created in combination with createPrefixUnaryExpression factory.
490
- // The method createNumericLiteral only accepts positive numbers
491
- // or those combined with createPrefixUnaryExpression.
492
- // Therefore, we need to ensure that the number is not negative.
493
- if (value < 0) {
494
- initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)))
495
- } else {
496
- initializer = factory.createNumericLiteral(value)
497
- }
498
- }
499
-
500
- if (typeof value === 'boolean') {
501
- initializer = value ? factory.createTrue() : factory.createFalse()
502
- }
503
-
504
- if (key) {
505
- return factory.createPropertyAssignment(factory.createStringLiteral(`${key}`), initializer)
506
- }
507
-
508
- return undefined
509
- })
510
- .filter(Boolean),
511
- true,
512
- ),
513
- factory.createTypeReferenceNode(factory.createIdentifier('const'), undefined),
514
- ),
515
- ),
516
- ],
517
- ts.NodeFlags.Const,
518
- ),
519
- ),
520
- factory.createTypeAliasDeclaration(
521
- type === 'asPascalConst' ? [] : [factory.createToken(ts.SyntaxKind.ExportKeyword)],
522
- factory.createIdentifier(typeName),
523
- undefined,
524
- factory.createIndexedAccessTypeNode(
525
- factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), undefined)),
526
- factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), undefined)),
527
- ),
528
- ),
529
- ]
530
- }
531
-
532
- export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array<string> | string; type: ts.TypeNode; nonNullable?: boolean }) {
533
- const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier('NonNullable'), [type]) : type
534
-
535
- if (Array.isArray(keys)) {
536
- return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [
537
- node,
538
- factory.createUnionTypeNode(
539
- keys.map((key) => {
540
- return factory.createLiteralTypeNode(factory.createStringLiteral(key))
541
- }),
542
- ),
543
- ])
544
- }
545
-
546
- return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))])
547
- }
548
-
549
- export const keywordTypeNodes = {
550
- any: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
551
- unknown: factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
552
- void: factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
553
- number: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
554
- integer: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
555
- object: factory.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword),
556
- string: factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
557
- boolean: factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword),
558
- undefined: factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
559
- null: factory.createLiteralTypeNode(factory.createToken(ts.SyntaxKind.NullKeyword)),
560
- } as const
561
-
562
- export const createTypeLiteralNode = factory.createTypeLiteralNode
563
-
564
- export const createTypeReferenceNode = factory.createTypeReferenceNode
565
- export const createNumericLiteral = factory.createNumericLiteral
566
- export const createStringLiteral = factory.createStringLiteral
567
-
568
- export const createArrayTypeNode = factory.createArrayTypeNode
569
-
570
- export const createLiteralTypeNode = factory.createLiteralTypeNode
571
- export const createNull = factory.createNull
572
- export const createIdentifier = factory.createIdentifier
573
-
574
- export const createOptionalTypeNode = factory.createOptionalTypeNode
575
- export const createTupleTypeNode = factory.createTupleTypeNode
576
- export const createRestTypeNode = factory.createRestTypeNode
577
- export const createTrue = factory.createTrue
578
- export const createFalse = factory.createFalse
package/src/format.ts DELETED
@@ -1,25 +0,0 @@
1
- import type { Options } from 'prettier'
2
- import { format as prettierFormat } from 'prettier'
3
- import pluginTypescript from 'prettier/plugins/typescript'
4
-
5
- const formatOptions: Options = {
6
- tabWidth: 2,
7
- printWidth: 160,
8
- parser: 'typescript',
9
- singleQuote: true,
10
- semi: false,
11
- bracketSameLine: false,
12
- endOfLine: 'auto',
13
- plugins: [pluginTypescript],
14
- }
15
- export function format(source?: string): Promise<string> {
16
- if (!source) {
17
- return Promise.resolve('')
18
- }
19
-
20
- try {
21
- return prettierFormat(source, formatOptions)
22
- } catch (_e) {
23
- return Promise.resolve(source)
24
- }
25
- }
package/src/print.ts DELETED
@@ -1,48 +0,0 @@
1
- import ts from 'typescript'
2
-
3
- const { factory } = ts
4
-
5
- export type PrintOptions = {
6
- source?: string
7
- baseName?: string
8
- scriptKind?: ts.ScriptKind
9
- }
10
-
11
- /**
12
- * Escaped new lines in code with block comments so they can be restored by {@link restoreNewLines}
13
- */
14
- const escapeNewLines = (code: string) => code.replace(/\n\n/g, '\n/* :newline: */')
15
-
16
- /**
17
- * Reverses {@link escapeNewLines} and restores new lines
18
- */
19
- const restoreNewLines = (code: string) => code.replace(/\/\* :newline: \*\//g, '\n')
20
-
21
- /**
22
- * Convert AST TypeScript/TSX nodes to a string based on the TypeScript printer.
23
- * Ensures consistent output across environments.
24
- * Also works as a formatter when `source` is provided without `elements`.
25
- */
26
- export function print(elements: Array<ts.Node> = [], { source = '', baseName = 'print.tsx', scriptKind = ts.ScriptKind.TSX }: PrintOptions = {}): string {
27
- const sourceFile = ts.createSourceFile(baseName, escapeNewLines(source), ts.ScriptTarget.ES2022, true, scriptKind)
28
-
29
- const printer = ts.createPrinter({
30
- omitTrailingSemicolon: true,
31
- newLine: ts.NewLineKind.LineFeed,
32
- removeComments: false,
33
- noEmitHelpers: true,
34
- })
35
-
36
- let output: string
37
-
38
- if (elements.length > 0) {
39
- // Print only provided nodes
40
- const nodes = elements.filter(Boolean).sort((a, b) => (a.pos ?? 0) - (b.pos ?? 0))
41
- output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(nodes), sourceFile)
42
- } else {
43
- // Format the whole file
44
- output = printer.printFile(sourceFile)
45
- }
46
-
47
- return restoreNewLines(output).replace(/\r\n/g, '\n')
48
- }