@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/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
+ };