@ontrails/source 0.2.0
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/CHANGELOG.md +179 -0
- package/README.md +96 -0
- package/package.json +34 -0
- package/src/collection.ts +329 -0
- package/src/edits.ts +57 -0
- package/src/index.ts +25 -0
- package/src/literals.ts +234 -0
- package/src/locations.ts +35 -0
- package/src/nodes.ts +661 -0
- package/src/parse.ts +58 -0
- package/src/scopes.ts +517 -0
- package/src/trails.ts +826 -0
- package/src/walk.ts +87 -0
package/src/nodes.ts
ADDED
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
/** Shared curated AST node types, guards, and field accessors. */
|
|
2
|
+
|
|
3
|
+
export interface AstNode {
|
|
4
|
+
readonly type: string;
|
|
5
|
+
readonly start: number;
|
|
6
|
+
readonly end: number;
|
|
7
|
+
readonly parent?: AstNode | null;
|
|
8
|
+
readonly key?: unknown;
|
|
9
|
+
readonly value?: unknown;
|
|
10
|
+
readonly body?: AstNode | readonly AstNode[];
|
|
11
|
+
readonly [key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ProgramNode extends AstNode {
|
|
15
|
+
readonly type: 'Program';
|
|
16
|
+
readonly body?: readonly AstNode[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IdentifierNode extends AstNode {
|
|
20
|
+
readonly type: 'Identifier';
|
|
21
|
+
readonly name?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface StringLiteralNode extends AstNode {
|
|
25
|
+
readonly type: 'Literal' | 'StringLiteral';
|
|
26
|
+
readonly value?: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CallExpressionNode extends AstNode {
|
|
30
|
+
readonly type: 'CallExpression' | 'NewExpression';
|
|
31
|
+
readonly arguments?: readonly AstNode[];
|
|
32
|
+
readonly callee?: AstNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MemberExpressionNode extends AstNode {
|
|
36
|
+
readonly type: 'MemberExpression' | 'StaticMemberExpression';
|
|
37
|
+
readonly computed?: boolean;
|
|
38
|
+
readonly object?: AstNode;
|
|
39
|
+
readonly property?: AstNode;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ImportDeclarationNode extends AstNode {
|
|
43
|
+
readonly importKind?: string;
|
|
44
|
+
readonly type: 'ImportDeclaration';
|
|
45
|
+
readonly source?: AstNode;
|
|
46
|
+
readonly specifiers?: readonly AstNode[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ImportSpecifierNode extends AstNode {
|
|
50
|
+
readonly importKind?: string;
|
|
51
|
+
readonly type:
|
|
52
|
+
| 'ImportDefaultSpecifier'
|
|
53
|
+
| 'ImportNamespaceSpecifier'
|
|
54
|
+
| 'ImportSpecifier';
|
|
55
|
+
readonly imported?: AstNode;
|
|
56
|
+
readonly local?: AstNode;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ExportDeclarationNode extends AstNode {
|
|
60
|
+
readonly type:
|
|
61
|
+
| 'ExportAllDeclaration'
|
|
62
|
+
| 'ExportDefaultDeclaration'
|
|
63
|
+
| 'ExportNamedDeclaration';
|
|
64
|
+
readonly declaration?: AstNode;
|
|
65
|
+
readonly source?: AstNode;
|
|
66
|
+
readonly specifiers?: readonly AstNode[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ExportSpecifierNode extends AstNode {
|
|
70
|
+
readonly type: 'ExportSpecifier';
|
|
71
|
+
readonly exportKind?: string;
|
|
72
|
+
readonly exported?: AstNode;
|
|
73
|
+
readonly local?: AstNode;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface VariableDeclarationNode extends AstNode {
|
|
77
|
+
readonly type: 'VariableDeclaration';
|
|
78
|
+
readonly declarations?: readonly AstNode[];
|
|
79
|
+
readonly kind?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface VariableDeclaratorNode extends AstNode {
|
|
83
|
+
readonly type: 'VariableDeclarator';
|
|
84
|
+
readonly id?: AstNode;
|
|
85
|
+
readonly init?: AstNode;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface DeclarationWithIdNode extends AstNode {
|
|
89
|
+
readonly type:
|
|
90
|
+
| 'ClassDeclaration'
|
|
91
|
+
| 'EnumDeclaration'
|
|
92
|
+
| 'FunctionDeclaration'
|
|
93
|
+
| 'InterfaceDeclaration'
|
|
94
|
+
| 'TSInterfaceDeclaration'
|
|
95
|
+
| 'TSEnumDeclaration'
|
|
96
|
+
| 'TSTypeAliasDeclaration';
|
|
97
|
+
readonly id?: AstNode;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ClassMemberNode extends AstNode {
|
|
101
|
+
readonly type: 'MethodDefinition' | 'PropertyDefinition';
|
|
102
|
+
readonly computed?: boolean;
|
|
103
|
+
readonly key?: AstNode;
|
|
104
|
+
readonly value?: AstNode;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ArrayExpressionNode extends AstNode {
|
|
108
|
+
readonly type: 'ArrayExpression';
|
|
109
|
+
readonly elements?: readonly (AstNode | null)[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ObjectExpressionNode extends AstNode {
|
|
113
|
+
readonly type: 'ObjectExpression' | 'ObjectPattern';
|
|
114
|
+
readonly properties?: readonly AstNode[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface PropertyNode extends AstNode {
|
|
118
|
+
readonly type: 'Property';
|
|
119
|
+
readonly computed?: boolean;
|
|
120
|
+
readonly key?: AstNode;
|
|
121
|
+
readonly value?: AstNode;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface RestElementNode extends AstNode {
|
|
125
|
+
readonly type: 'RestElement';
|
|
126
|
+
readonly argument?: AstNode;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface AssignmentPatternNode extends AstNode {
|
|
130
|
+
readonly type: 'AssignmentPattern';
|
|
131
|
+
readonly left?: AstNode;
|
|
132
|
+
readonly right?: AstNode;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface ExpressionStatementNode extends AstNode {
|
|
136
|
+
readonly type: 'ExpressionStatement';
|
|
137
|
+
readonly expression?: AstNode;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface UnaryExpressionNode extends AstNode {
|
|
141
|
+
readonly type: 'AwaitExpression' | 'ChainExpression' | 'UnaryExpression';
|
|
142
|
+
readonly argument?: AstNode;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface BinaryExpressionNode extends AstNode {
|
|
146
|
+
readonly type:
|
|
147
|
+
| 'AssignmentExpression'
|
|
148
|
+
| 'BinaryExpression'
|
|
149
|
+
| 'ConditionalExpression'
|
|
150
|
+
| 'LogicalExpression';
|
|
151
|
+
readonly alternate?: AstNode;
|
|
152
|
+
readonly consequent?: AstNode;
|
|
153
|
+
readonly left?: AstNode;
|
|
154
|
+
readonly operator?: string;
|
|
155
|
+
readonly right?: AstNode;
|
|
156
|
+
readonly test?: AstNode;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface FunctionLikeNode extends AstNode {
|
|
160
|
+
readonly type:
|
|
161
|
+
| 'ArrowFunctionExpression'
|
|
162
|
+
| 'FunctionDeclaration'
|
|
163
|
+
| 'FunctionExpression';
|
|
164
|
+
readonly body?: AstNode;
|
|
165
|
+
readonly params?: readonly AstNode[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface BlockStatementNode extends AstNode {
|
|
169
|
+
readonly type: 'BlockStatement' | 'StaticBlock';
|
|
170
|
+
readonly body?: readonly AstNode[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ReturnStatementNode extends AstNode {
|
|
174
|
+
readonly type: 'ReturnStatement';
|
|
175
|
+
readonly argument?: AstNode;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export type CuratedAstNode =
|
|
179
|
+
| ArrayExpressionNode
|
|
180
|
+
| AssignmentPatternNode
|
|
181
|
+
| BinaryExpressionNode
|
|
182
|
+
| BlockStatementNode
|
|
183
|
+
| CallExpressionNode
|
|
184
|
+
| ClassMemberNode
|
|
185
|
+
| DeclarationWithIdNode
|
|
186
|
+
| ExportDeclarationNode
|
|
187
|
+
| ExportSpecifierNode
|
|
188
|
+
| ExpressionStatementNode
|
|
189
|
+
| FunctionLikeNode
|
|
190
|
+
| IdentifierNode
|
|
191
|
+
| ImportDeclarationNode
|
|
192
|
+
| ImportSpecifierNode
|
|
193
|
+
| MemberExpressionNode
|
|
194
|
+
| ObjectExpressionNode
|
|
195
|
+
| ProgramNode
|
|
196
|
+
| PropertyNode
|
|
197
|
+
| RestElementNode
|
|
198
|
+
| ReturnStatementNode
|
|
199
|
+
| StringLiteralNode
|
|
200
|
+
| UnaryExpressionNode
|
|
201
|
+
| VariableDeclarationNode
|
|
202
|
+
| VariableDeclaratorNode;
|
|
203
|
+
|
|
204
|
+
export interface AstParentContext {
|
|
205
|
+
readonly index: number | null;
|
|
206
|
+
readonly key: string | number | symbol | null | undefined;
|
|
207
|
+
readonly parent: AstNode | null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface AstScopeDeclaration {
|
|
211
|
+
readonly end: number;
|
|
212
|
+
readonly node: AstNode;
|
|
213
|
+
readonly start: number;
|
|
214
|
+
readonly type: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface AstScopeContext extends AstParentContext {
|
|
218
|
+
readonly currentScope: string;
|
|
219
|
+
readonly getDeclaration: (name: string) => AstScopeDeclaration | null;
|
|
220
|
+
readonly isDeclared: (name: string) => boolean;
|
|
221
|
+
readonly isCurrentScopeUnder: (scope: string) => boolean;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface SourceEdit {
|
|
225
|
+
readonly end: number;
|
|
226
|
+
readonly replacement: string;
|
|
227
|
+
readonly start: number;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface SourceLocation {
|
|
231
|
+
readonly column: number;
|
|
232
|
+
readonly line: number;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface AstParseDiagnosticLabel {
|
|
236
|
+
readonly end: number;
|
|
237
|
+
readonly message: string | null;
|
|
238
|
+
readonly start: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface AstParseDiagnostic {
|
|
242
|
+
readonly helpMessage: string | null;
|
|
243
|
+
readonly labels: readonly AstParseDiagnosticLabel[];
|
|
244
|
+
readonly message: string;
|
|
245
|
+
readonly severity: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Parser-native source comment span. Offsets include the comment delimiters. */
|
|
249
|
+
export interface SourceComment {
|
|
250
|
+
readonly end: number;
|
|
251
|
+
readonly start: number;
|
|
252
|
+
readonly type: 'Block' | 'Line';
|
|
253
|
+
/** Comment text without the line or block delimiters. */
|
|
254
|
+
readonly value: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface AstParseResult {
|
|
258
|
+
readonly ast: AstNode | null;
|
|
259
|
+
/** Exact parser-native comments for a diagnostic-free parse; empty on errors. */
|
|
260
|
+
readonly comments: readonly SourceComment[];
|
|
261
|
+
readonly diagnostics: readonly AstParseDiagnostic[];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface AstFieldView {
|
|
265
|
+
readonly alternate?: AstNode;
|
|
266
|
+
readonly argument?: AstNode;
|
|
267
|
+
readonly arguments?: readonly AstNode[];
|
|
268
|
+
readonly body?: AstNode | readonly AstNode[];
|
|
269
|
+
readonly callee?: AstNode;
|
|
270
|
+
readonly cases?: readonly AstNode[];
|
|
271
|
+
readonly computed?: boolean;
|
|
272
|
+
readonly consequent?: AstNode;
|
|
273
|
+
readonly declaration?: AstNode;
|
|
274
|
+
readonly declarations?: readonly AstNode[];
|
|
275
|
+
readonly discriminant?: AstNode;
|
|
276
|
+
readonly elements?: readonly (AstNode | null)[];
|
|
277
|
+
readonly exportKind?: string;
|
|
278
|
+
readonly exported?: AstNode;
|
|
279
|
+
readonly expression?: AstNode;
|
|
280
|
+
readonly id?: AstNode;
|
|
281
|
+
readonly imported?: AstNode;
|
|
282
|
+
readonly importKind?: string;
|
|
283
|
+
readonly init?: AstNode;
|
|
284
|
+
readonly key?: AstNode;
|
|
285
|
+
readonly kind?: string;
|
|
286
|
+
readonly left?: AstNode;
|
|
287
|
+
readonly local?: AstNode;
|
|
288
|
+
readonly name?: string;
|
|
289
|
+
readonly object?: AstNode;
|
|
290
|
+
readonly operator?: string;
|
|
291
|
+
readonly param?: AstNode;
|
|
292
|
+
readonly params?: readonly AstNode[];
|
|
293
|
+
readonly properties?: readonly AstNode[];
|
|
294
|
+
readonly property?: AstNode;
|
|
295
|
+
readonly returnType?: AstNode;
|
|
296
|
+
readonly right?: AstNode;
|
|
297
|
+
readonly source?: AstNode;
|
|
298
|
+
readonly specifiers?: readonly AstNode[];
|
|
299
|
+
readonly superClass?: AstNode;
|
|
300
|
+
readonly test?: AstNode;
|
|
301
|
+
readonly typeAnnotation?: AstNode;
|
|
302
|
+
readonly value?: unknown;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export const isAstNode = (value: unknown): value is AstNode =>
|
|
306
|
+
Boolean(value && typeof value === 'object' && (value as AstNode).type);
|
|
307
|
+
|
|
308
|
+
const isNodeType = <TNode extends CuratedAstNode>(
|
|
309
|
+
node: AstNode | null | undefined,
|
|
310
|
+
types: readonly string[]
|
|
311
|
+
): node is TNode =>
|
|
312
|
+
node !== null && node !== undefined && types.includes(node.type);
|
|
313
|
+
|
|
314
|
+
export const isProgram = (
|
|
315
|
+
node: AstNode | null | undefined
|
|
316
|
+
): node is ProgramNode => isNodeType<ProgramNode>(node, ['Program']);
|
|
317
|
+
|
|
318
|
+
export const isIdentifier = (
|
|
319
|
+
node: AstNode | null | undefined
|
|
320
|
+
): node is IdentifierNode => isNodeType<IdentifierNode>(node, ['Identifier']);
|
|
321
|
+
|
|
322
|
+
export const isCallExpression = (
|
|
323
|
+
node: AstNode | null | undefined
|
|
324
|
+
): node is CallExpressionNode =>
|
|
325
|
+
isNodeType<CallExpressionNode>(node, ['CallExpression', 'NewExpression']);
|
|
326
|
+
|
|
327
|
+
export const isMemberExpression = (
|
|
328
|
+
node: AstNode | null | undefined
|
|
329
|
+
): node is MemberExpressionNode =>
|
|
330
|
+
isNodeType<MemberExpressionNode>(node, [
|
|
331
|
+
'MemberExpression',
|
|
332
|
+
'StaticMemberExpression',
|
|
333
|
+
]);
|
|
334
|
+
|
|
335
|
+
export const isImportDeclaration = (
|
|
336
|
+
node: AstNode | null | undefined
|
|
337
|
+
): node is ImportDeclarationNode =>
|
|
338
|
+
isNodeType<ImportDeclarationNode>(node, ['ImportDeclaration']);
|
|
339
|
+
|
|
340
|
+
export const isImportSpecifier = (
|
|
341
|
+
node: AstNode | null | undefined
|
|
342
|
+
): node is ImportSpecifierNode =>
|
|
343
|
+
isNodeType<ImportSpecifierNode>(node, [
|
|
344
|
+
'ImportDefaultSpecifier',
|
|
345
|
+
'ImportNamespaceSpecifier',
|
|
346
|
+
'ImportSpecifier',
|
|
347
|
+
]);
|
|
348
|
+
|
|
349
|
+
export const isExportDeclaration = (
|
|
350
|
+
node: AstNode | null | undefined
|
|
351
|
+
): node is ExportDeclarationNode =>
|
|
352
|
+
isNodeType<ExportDeclarationNode>(node, [
|
|
353
|
+
'ExportAllDeclaration',
|
|
354
|
+
'ExportDefaultDeclaration',
|
|
355
|
+
'ExportNamedDeclaration',
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
export const isExportNamedDeclaration = (
|
|
359
|
+
node: AstNode | null | undefined
|
|
360
|
+
): node is ExportDeclarationNode & {
|
|
361
|
+
readonly type: 'ExportNamedDeclaration';
|
|
362
|
+
} =>
|
|
363
|
+
isNodeType<
|
|
364
|
+
ExportDeclarationNode & { readonly type: 'ExportNamedDeclaration' }
|
|
365
|
+
>(node, ['ExportNamedDeclaration']);
|
|
366
|
+
|
|
367
|
+
export const isExportDefaultDeclaration = (
|
|
368
|
+
node: AstNode | null | undefined
|
|
369
|
+
): node is ExportDeclarationNode & {
|
|
370
|
+
readonly type: 'ExportDefaultDeclaration';
|
|
371
|
+
} =>
|
|
372
|
+
isNodeType<
|
|
373
|
+
ExportDeclarationNode & { readonly type: 'ExportDefaultDeclaration' }
|
|
374
|
+
>(node, ['ExportDefaultDeclaration']);
|
|
375
|
+
|
|
376
|
+
export const isExportAllDeclaration = (
|
|
377
|
+
node: AstNode | null | undefined
|
|
378
|
+
): node is ExportDeclarationNode & { readonly type: 'ExportAllDeclaration' } =>
|
|
379
|
+
isNodeType<ExportDeclarationNode & { readonly type: 'ExportAllDeclaration' }>(
|
|
380
|
+
node,
|
|
381
|
+
['ExportAllDeclaration']
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
export const isExportSpecifier = (
|
|
385
|
+
node: AstNode | null | undefined
|
|
386
|
+
): node is ExportSpecifierNode =>
|
|
387
|
+
isNodeType<ExportSpecifierNode>(node, ['ExportSpecifier']);
|
|
388
|
+
|
|
389
|
+
export const isVariableDeclaration = (
|
|
390
|
+
node: AstNode | null | undefined
|
|
391
|
+
): node is VariableDeclarationNode =>
|
|
392
|
+
isNodeType<VariableDeclarationNode>(node, ['VariableDeclaration']);
|
|
393
|
+
|
|
394
|
+
export const isVariableDeclarator = (
|
|
395
|
+
node: AstNode | null | undefined
|
|
396
|
+
): node is VariableDeclaratorNode =>
|
|
397
|
+
isNodeType<VariableDeclaratorNode>(node, ['VariableDeclarator']);
|
|
398
|
+
|
|
399
|
+
export const isDeclarationWithId = (
|
|
400
|
+
node: AstNode | null | undefined
|
|
401
|
+
): node is DeclarationWithIdNode =>
|
|
402
|
+
isNodeType<DeclarationWithIdNode>(node, [
|
|
403
|
+
'ClassDeclaration',
|
|
404
|
+
'EnumDeclaration',
|
|
405
|
+
'FunctionDeclaration',
|
|
406
|
+
'InterfaceDeclaration',
|
|
407
|
+
'TSInterfaceDeclaration',
|
|
408
|
+
'TSEnumDeclaration',
|
|
409
|
+
'TSTypeAliasDeclaration',
|
|
410
|
+
]);
|
|
411
|
+
|
|
412
|
+
export const isClassMember = (
|
|
413
|
+
node: AstNode | null | undefined
|
|
414
|
+
): node is ClassMemberNode =>
|
|
415
|
+
isNodeType<ClassMemberNode>(node, ['MethodDefinition', 'PropertyDefinition']);
|
|
416
|
+
|
|
417
|
+
export const isArrayExpression = (
|
|
418
|
+
node: AstNode | null | undefined
|
|
419
|
+
): node is ArrayExpressionNode =>
|
|
420
|
+
isNodeType<ArrayExpressionNode>(node, ['ArrayExpression']);
|
|
421
|
+
|
|
422
|
+
export const isObjectExpression = (
|
|
423
|
+
node: AstNode | null | undefined
|
|
424
|
+
): node is ObjectExpressionNode =>
|
|
425
|
+
isNodeType<ObjectExpressionNode>(node, ['ObjectExpression', 'ObjectPattern']);
|
|
426
|
+
|
|
427
|
+
export const isProperty = (
|
|
428
|
+
node: AstNode | null | undefined
|
|
429
|
+
): node is PropertyNode => isNodeType<PropertyNode>(node, ['Property']);
|
|
430
|
+
|
|
431
|
+
export const isRestElement = (
|
|
432
|
+
node: AstNode | null | undefined
|
|
433
|
+
): node is RestElementNode =>
|
|
434
|
+
isNodeType<RestElementNode>(node, ['RestElement']);
|
|
435
|
+
|
|
436
|
+
export const isAssignmentPattern = (
|
|
437
|
+
node: AstNode | null | undefined
|
|
438
|
+
): node is AssignmentPatternNode =>
|
|
439
|
+
isNodeType<AssignmentPatternNode>(node, ['AssignmentPattern']);
|
|
440
|
+
|
|
441
|
+
export const isExpressionStatement = (
|
|
442
|
+
node: AstNode | null | undefined
|
|
443
|
+
): node is ExpressionStatementNode =>
|
|
444
|
+
isNodeType<ExpressionStatementNode>(node, ['ExpressionStatement']);
|
|
445
|
+
|
|
446
|
+
export const isUnaryExpression = (
|
|
447
|
+
node: AstNode | null | undefined
|
|
448
|
+
): node is UnaryExpressionNode =>
|
|
449
|
+
isNodeType<UnaryExpressionNode>(node, [
|
|
450
|
+
'AwaitExpression',
|
|
451
|
+
'ChainExpression',
|
|
452
|
+
'UnaryExpression',
|
|
453
|
+
]);
|
|
454
|
+
|
|
455
|
+
export const isBinaryExpression = (
|
|
456
|
+
node: AstNode | null | undefined
|
|
457
|
+
): node is BinaryExpressionNode =>
|
|
458
|
+
isNodeType<BinaryExpressionNode>(node, [
|
|
459
|
+
'AssignmentExpression',
|
|
460
|
+
'BinaryExpression',
|
|
461
|
+
'ConditionalExpression',
|
|
462
|
+
'LogicalExpression',
|
|
463
|
+
]);
|
|
464
|
+
|
|
465
|
+
export const isFunctionLike = (
|
|
466
|
+
node: AstNode | null | undefined
|
|
467
|
+
): node is FunctionLikeNode =>
|
|
468
|
+
isNodeType<FunctionLikeNode>(node, [
|
|
469
|
+
'ArrowFunctionExpression',
|
|
470
|
+
'FunctionDeclaration',
|
|
471
|
+
'FunctionExpression',
|
|
472
|
+
]);
|
|
473
|
+
|
|
474
|
+
export const isBlockStatement = (
|
|
475
|
+
node: AstNode | null | undefined
|
|
476
|
+
): node is BlockStatementNode =>
|
|
477
|
+
isNodeType<BlockStatementNode>(node, ['BlockStatement', 'StaticBlock']);
|
|
478
|
+
|
|
479
|
+
export const isReturnStatement = (
|
|
480
|
+
node: AstNode | null | undefined
|
|
481
|
+
): node is ReturnStatementNode =>
|
|
482
|
+
isNodeType<ReturnStatementNode>(node, ['ReturnStatement']);
|
|
483
|
+
|
|
484
|
+
const deriveAstFieldView = (
|
|
485
|
+
node: AstNode | null | undefined
|
|
486
|
+
): AstFieldView | null => (node ? (node as AstFieldView) : null);
|
|
487
|
+
|
|
488
|
+
export const getNodeAlternate = (
|
|
489
|
+
node: AstNode | null | undefined
|
|
490
|
+
): AstNode | undefined => deriveAstFieldView(node)?.alternate;
|
|
491
|
+
|
|
492
|
+
export const getNodeArgument = (
|
|
493
|
+
node: AstNode | null | undefined
|
|
494
|
+
): AstNode | undefined => deriveAstFieldView(node)?.argument;
|
|
495
|
+
|
|
496
|
+
export const getNodeArguments = (
|
|
497
|
+
node: AstNode | null | undefined
|
|
498
|
+
): readonly AstNode[] => (isCallExpression(node) ? (node.arguments ?? []) : []);
|
|
499
|
+
|
|
500
|
+
export const getNodeBody = (
|
|
501
|
+
node: AstNode | null | undefined
|
|
502
|
+
): AstNode | readonly AstNode[] | undefined => deriveAstFieldView(node)?.body;
|
|
503
|
+
|
|
504
|
+
export const getNodeBodyNode = (
|
|
505
|
+
node: AstNode | null | undefined
|
|
506
|
+
): AstNode | undefined => {
|
|
507
|
+
const body = getNodeBody(node);
|
|
508
|
+
return isAstNode(body) ? body : undefined;
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
export const getNodeBodyStatements = (
|
|
512
|
+
node: AstNode | null | undefined
|
|
513
|
+
): readonly AstNode[] => {
|
|
514
|
+
const body = getNodeBody(node);
|
|
515
|
+
return Array.isArray(body) ? body : [];
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
export const getNodeCallee = (
|
|
519
|
+
node: AstNode | null | undefined
|
|
520
|
+
): AstNode | undefined => (isCallExpression(node) ? node.callee : undefined);
|
|
521
|
+
|
|
522
|
+
export const getNodeCases = (
|
|
523
|
+
node: AstNode | null | undefined
|
|
524
|
+
): readonly AstNode[] => deriveAstFieldView(node)?.cases ?? [];
|
|
525
|
+
|
|
526
|
+
export const getNodeComputed = (
|
|
527
|
+
node: AstNode | null | undefined
|
|
528
|
+
): boolean | undefined => deriveAstFieldView(node)?.computed;
|
|
529
|
+
|
|
530
|
+
export const getNodeConsequent = (
|
|
531
|
+
node: AstNode | null | undefined
|
|
532
|
+
): AstNode | undefined => deriveAstFieldView(node)?.consequent;
|
|
533
|
+
|
|
534
|
+
export const getNodeDeclaration = (
|
|
535
|
+
node: AstNode | null | undefined
|
|
536
|
+
): AstNode | undefined => deriveAstFieldView(node)?.declaration;
|
|
537
|
+
|
|
538
|
+
export const getNodeDeclarations = (
|
|
539
|
+
node: AstNode | null | undefined
|
|
540
|
+
): readonly AstNode[] =>
|
|
541
|
+
isVariableDeclaration(node) ? (node.declarations ?? []) : [];
|
|
542
|
+
|
|
543
|
+
export const getNodeDiscriminant = (
|
|
544
|
+
node: AstNode | null | undefined
|
|
545
|
+
): AstNode | undefined => deriveAstFieldView(node)?.discriminant;
|
|
546
|
+
|
|
547
|
+
export const getNodeElements = (
|
|
548
|
+
node: AstNode | null | undefined
|
|
549
|
+
): readonly (AstNode | null)[] => deriveAstFieldView(node)?.elements ?? [];
|
|
550
|
+
|
|
551
|
+
export const getNodeExported = (
|
|
552
|
+
node: AstNode | null | undefined
|
|
553
|
+
): AstNode | undefined => deriveAstFieldView(node)?.exported;
|
|
554
|
+
|
|
555
|
+
export const getNodeExportKind = (
|
|
556
|
+
node: AstNode | null | undefined
|
|
557
|
+
): string | undefined => deriveAstFieldView(node)?.exportKind;
|
|
558
|
+
|
|
559
|
+
export const getNodeExpression = (
|
|
560
|
+
node: AstNode | null | undefined
|
|
561
|
+
): AstNode | undefined => deriveAstFieldView(node)?.expression;
|
|
562
|
+
|
|
563
|
+
export const getNodeId = (
|
|
564
|
+
node: AstNode | null | undefined
|
|
565
|
+
): AstNode | undefined => deriveAstFieldView(node)?.id;
|
|
566
|
+
|
|
567
|
+
export const getNodeImported = (
|
|
568
|
+
node: AstNode | null | undefined
|
|
569
|
+
): AstNode | undefined => deriveAstFieldView(node)?.imported;
|
|
570
|
+
|
|
571
|
+
export const getNodeImportKind = (
|
|
572
|
+
node: AstNode | null | undefined
|
|
573
|
+
): string | undefined => deriveAstFieldView(node)?.importKind;
|
|
574
|
+
|
|
575
|
+
export const getNodeInit = (
|
|
576
|
+
node: AstNode | null | undefined
|
|
577
|
+
): AstNode | undefined => deriveAstFieldView(node)?.init;
|
|
578
|
+
|
|
579
|
+
export const getNodeKey = (
|
|
580
|
+
node: AstNode | null | undefined
|
|
581
|
+
): AstNode | undefined => deriveAstFieldView(node)?.key;
|
|
582
|
+
|
|
583
|
+
export const getNodeKind = (
|
|
584
|
+
node: AstNode | null | undefined
|
|
585
|
+
): string | undefined => deriveAstFieldView(node)?.kind;
|
|
586
|
+
|
|
587
|
+
export const getNodeLeft = (
|
|
588
|
+
node: AstNode | null | undefined
|
|
589
|
+
): AstNode | undefined => deriveAstFieldView(node)?.left;
|
|
590
|
+
|
|
591
|
+
export const getNodeLocal = (
|
|
592
|
+
node: AstNode | null | undefined
|
|
593
|
+
): AstNode | undefined => deriveAstFieldView(node)?.local;
|
|
594
|
+
|
|
595
|
+
export const getNodeName = (
|
|
596
|
+
node: AstNode | null | undefined
|
|
597
|
+
): string | undefined => deriveAstFieldView(node)?.name;
|
|
598
|
+
|
|
599
|
+
export const getNodeObject = (
|
|
600
|
+
node: AstNode | null | undefined
|
|
601
|
+
): AstNode | undefined => (isMemberExpression(node) ? node.object : undefined);
|
|
602
|
+
|
|
603
|
+
export const getNodeOperator = (
|
|
604
|
+
node: AstNode | null | undefined
|
|
605
|
+
): string | undefined => deriveAstFieldView(node)?.operator;
|
|
606
|
+
|
|
607
|
+
export const getNodeParam = (
|
|
608
|
+
node: AstNode | null | undefined
|
|
609
|
+
): AstNode | undefined => deriveAstFieldView(node)?.param;
|
|
610
|
+
|
|
611
|
+
export const getNodeParams = (
|
|
612
|
+
node: AstNode | null | undefined
|
|
613
|
+
): readonly AstNode[] => (isFunctionLike(node) ? (node.params ?? []) : []);
|
|
614
|
+
|
|
615
|
+
export const getNodeProperties = (
|
|
616
|
+
node: AstNode | null | undefined
|
|
617
|
+
): readonly AstNode[] =>
|
|
618
|
+
isObjectExpression(node) ? (node.properties ?? []) : [];
|
|
619
|
+
|
|
620
|
+
export const getNodeProperty = (
|
|
621
|
+
node: AstNode | null | undefined
|
|
622
|
+
): AstNode | undefined =>
|
|
623
|
+
isMemberExpression(node) ? node.property : undefined;
|
|
624
|
+
|
|
625
|
+
export const getNodeReturnType = (
|
|
626
|
+
node: AstNode | null | undefined
|
|
627
|
+
): AstNode | undefined => deriveAstFieldView(node)?.returnType;
|
|
628
|
+
|
|
629
|
+
export const getNodeRight = (
|
|
630
|
+
node: AstNode | null | undefined
|
|
631
|
+
): AstNode | undefined => deriveAstFieldView(node)?.right;
|
|
632
|
+
|
|
633
|
+
export const getNodeSource = (
|
|
634
|
+
node: AstNode | null | undefined
|
|
635
|
+
): AstNode | undefined => deriveAstFieldView(node)?.source;
|
|
636
|
+
|
|
637
|
+
export const getNodeSpecifiers = (
|
|
638
|
+
node: AstNode | null | undefined
|
|
639
|
+
): readonly AstNode[] => deriveAstFieldView(node)?.specifiers ?? [];
|
|
640
|
+
|
|
641
|
+
export const getNodeSuperClass = (
|
|
642
|
+
node: AstNode | null | undefined
|
|
643
|
+
): AstNode | undefined => deriveAstFieldView(node)?.superClass;
|
|
644
|
+
|
|
645
|
+
export const getNodeTest = (
|
|
646
|
+
node: AstNode | null | undefined
|
|
647
|
+
): AstNode | undefined => deriveAstFieldView(node)?.test;
|
|
648
|
+
|
|
649
|
+
export const getNodeTypeAnnotation = (
|
|
650
|
+
node: AstNode | null | undefined
|
|
651
|
+
): AstNode | undefined => deriveAstFieldView(node)?.typeAnnotation;
|
|
652
|
+
|
|
653
|
+
export const getNodeValue = (node: AstNode | null | undefined): unknown =>
|
|
654
|
+
deriveAstFieldView(node)?.value;
|
|
655
|
+
|
|
656
|
+
export const getNodeValueNode = (
|
|
657
|
+
node: AstNode | null | undefined
|
|
658
|
+
): AstNode | undefined => {
|
|
659
|
+
const value = getNodeValue(node);
|
|
660
|
+
return isAstNode(value) ? value : undefined;
|
|
661
|
+
};
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Shared OXC parser facade. */
|
|
2
|
+
|
|
3
|
+
import { parseSync } from 'oxc-parser';
|
|
4
|
+
|
|
5
|
+
import type { AstNode, AstParseResult } from './nodes.js';
|
|
6
|
+
|
|
7
|
+
/** Parse TypeScript source into an AST. Returns null on parse failure. */
|
|
8
|
+
export const parse = (filePath: string, sourceCode: string): AstNode | null => {
|
|
9
|
+
try {
|
|
10
|
+
const result = parseSync(filePath, sourceCode, { sourceType: 'module' });
|
|
11
|
+
return result.program as unknown as AstNode;
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse TypeScript source and surface parser diagnostics. OXC can recover a
|
|
19
|
+
* partial program for malformed input, so rewrite tooling should use this
|
|
20
|
+
* helper when applying edits would be unsafe after syntax errors.
|
|
21
|
+
*/
|
|
22
|
+
export const parseWithDiagnostics = (
|
|
23
|
+
filePath: string,
|
|
24
|
+
sourceCode: string
|
|
25
|
+
): AstParseResult => {
|
|
26
|
+
try {
|
|
27
|
+
const result = parseSync(filePath, sourceCode, { sourceType: 'module' });
|
|
28
|
+
const diagnostics = result.errors.map((error) => ({
|
|
29
|
+
helpMessage: error.helpMessage,
|
|
30
|
+
labels: error.labels.map((label) => ({
|
|
31
|
+
end: label.end,
|
|
32
|
+
message: label.message,
|
|
33
|
+
start: label.start,
|
|
34
|
+
})),
|
|
35
|
+
message: error.message,
|
|
36
|
+
severity: error.severity,
|
|
37
|
+
}));
|
|
38
|
+
return {
|
|
39
|
+
ast: result.program as unknown as AstNode,
|
|
40
|
+
comments: diagnostics.length === 0 ? result.comments : [],
|
|
41
|
+
diagnostics,
|
|
42
|
+
};
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
ast: null,
|
|
46
|
+
comments: [],
|
|
47
|
+
diagnostics: [
|
|
48
|
+
{
|
|
49
|
+
helpMessage: null,
|
|
50
|
+
labels: [],
|
|
51
|
+
message:
|
|
52
|
+
error instanceof Error ? error.message : 'Unable to parse source.',
|
|
53
|
+
severity: 'Error',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
};
|