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