0
# AST Traversal and Analysis
1
2
Comprehensive utilities for navigating, analyzing, and manipulating TypeScript AST nodes. These functions provide efficient traversal patterns, token iteration, comment processing, position mapping, and node relationship analysis.
3
4
## Capabilities
5
6
### Token Iteration
7
8
Utilities for iterating through tokens and trivia in TypeScript source code.
9
10
```typescript { .api }
11
/**
12
* Iterate through all tokens in a node
13
* @param node - AST node to traverse
14
* @param cb - Callback function called for each token
15
* @param sourceFile - Source file (optional, inferred from node if not provided)
16
*/
17
function forEachToken(node: ts.Node, cb: (node: ts.Node) => void, sourceFile?: ts.SourceFile): void;
18
19
/**
20
* Iterate through all tokens including trivia (whitespace, comments)
21
* @param node - AST node to traverse
22
* @param cb - Callback function called for each token with trivia information
23
* @param sourceFile - Source file (optional, inferred from node if not provided)
24
*/
25
function forEachTokenWithTrivia(node: ts.Node, cb: ForEachTokenCallback, sourceFile?: ts.SourceFile): void;
26
27
/**
28
* Callback type for token iteration with trivia
29
* @param fullText - Full text of the source file
30
* @param kind - Syntax kind of the token
31
* @param range - Text range of the token
32
* @param parent - Parent node of the token
33
*/
34
type ForEachTokenCallback = (fullText: string, kind: ts.SyntaxKind, range: ts.TextRange, parent: ts.Node) => void;
35
```
36
37
### Comment Processing
38
39
Utilities for working with comments in TypeScript source code.
40
41
```typescript { .api }
42
/**
43
* Iterate through all comments in a node
44
* @param node - AST node to search for comments
45
* @param cb - Callback function called for each comment
46
* @param sourceFile - Source file (optional, inferred from node if not provided)
47
*/
48
function forEachComment(node: ts.Node, cb: ForEachCommentCallback, sourceFile?: ts.SourceFile): void;
49
50
/**
51
* Callback type for comment iteration
52
* @param fullText - Full text of the source file
53
* @param comment - Comment range information
54
*/
55
type ForEachCommentCallback = (fullText: string, comment: ts.CommentRange) => void;
56
57
/**
58
* Get comment at specific position
59
* @param sourceFile - Source file to search
60
* @param pos - Position to check for comments
61
* @param parent - Optional parent node to limit search scope
62
* @returns Comment range if found, undefined otherwise
63
*/
64
function getCommentAtPosition(sourceFile: ts.SourceFile, pos: number, parent?: ts.Node): ts.CommentRange | undefined;
65
66
/**
67
* Check if position is inside a comment
68
* @param sourceFile - Source file to check
69
* @param pos - Position to check
70
* @param parent - Optional parent node to limit search scope
71
* @returns true if position is within a comment
72
*/
73
function isPositionInComment(sourceFile: ts.SourceFile, pos: number, parent?: ts.Node): boolean;
74
75
/**
76
* Extract text content from a comment range
77
* @param sourceText - Full source text
78
* @param comment - Comment range to extract
79
* @returns Comment text without comment syntax markers
80
*/
81
function commentText(sourceText: string, comment: ts.CommentRange): string;
82
```
83
84
### Node Navigation
85
86
Utilities for navigating between related AST nodes.
87
88
```typescript { .api }
89
/**
90
* Get the previous token before a node
91
* @param node - Starting node
92
* @param sourceFile - Source file (optional, inferred from node if not provided)
93
* @returns Previous token or undefined if none exists
94
*/
95
function getPreviousToken(node: ts.Node, sourceFile?: ts.SourceFile): ts.Node | undefined;
96
97
/**
98
* Get the next token after a node
99
* @param node - Starting node
100
* @param sourceFile - Source file (optional, inferred from node if not provided)
101
* @returns Next token or undefined if none exists
102
*/
103
function getNextToken(node: ts.Node, sourceFile?: ts.SourceFile): ts.Node | undefined;
104
105
/**
106
* Get token at or following a specific position
107
* @param parent - Parent node to search within
108
* @param pos - Position to find token at
109
* @param sourceFile - Source file (optional, inferred from parent if not provided)
110
* @param allowJsDoc - Whether to include JSDoc comments in search
111
* @returns Token at position or undefined if none found
112
*/
113
function getTokenAtPosition(parent: ts.Node, pos: number, sourceFile?: ts.SourceFile, allowJsDoc?: boolean): ts.Node | undefined;
114
115
/**
116
* Find child node of specific syntax kind
117
* @param node - Parent node to search
118
* @param kind - Syntax kind to find
119
* @param sourceFile - Source file (optional, inferred from node if not provided)
120
* @returns Child token of specified kind or undefined
121
*/
122
function getChildOfKind<T extends ts.SyntaxKind>(node: ts.Node, kind: T, sourceFile?: ts.SourceFile): ts.Token<T> | undefined;
123
```
124
125
### Statement Navigation
126
127
Utilities for navigating between statements in block-like structures.
128
129
```typescript { .api }
130
/**
131
* Get the previous statement in a block
132
* @param statement - Current statement
133
* @returns Previous statement or undefined if first statement
134
*/
135
function getPreviousStatement(statement: ts.Statement): ts.Statement | undefined;
136
137
/**
138
* Get the next statement in a block
139
* @param statement - Current statement
140
* @returns Next statement or undefined if last statement
141
*/
142
function getNextStatement(statement: ts.Statement): ts.Statement | undefined;
143
```
144
145
### Position-based Analysis
146
147
Utilities for finding nodes and analyzing positions within source code.
148
149
```typescript { .api }
150
/**
151
* Get the deepest AST node at a specific position
152
* @param node - Root node to search from
153
* @param pos - Position to find node at
154
* @returns Deepest node at position or undefined if position is outside node range
155
*/
156
function getAstNodeAtPosition(node: ts.Node, pos: number): ts.Node | undefined;
157
158
/**
159
* Get wrapped node at position (for converted AST)
160
* @param wrap - Wrapped node to search
161
* @param pos - Position to find node at
162
* @returns Wrapped node at position or undefined
163
*/
164
function getWrappedNodeAtPosition(wrap: NodeWrap, pos: number): NodeWrap | undefined;
165
```
166
167
### Syntax Kind Utilities
168
169
Utilities for checking and categorizing syntax kinds.
170
171
```typescript { .api }
172
/**
173
* Check if syntax kind represents a token
174
* @param kind - Syntax kind to check
175
* @returns true if kind is a token
176
*/
177
function isTokenKind(kind: ts.SyntaxKind): boolean;
178
179
/**
180
* Check if syntax kind represents a node
181
* @param kind - Syntax kind to check
182
* @returns true if kind is a node
183
*/
184
function isNodeKind(kind: ts.SyntaxKind): boolean;
185
186
/**
187
* Check if syntax kind represents an assignment operator
188
* @param kind - Syntax kind to check
189
* @returns true if kind is an assignment operator
190
*/
191
function isAssignmentKind(kind: ts.SyntaxKind): boolean;
192
193
/**
194
* Check if syntax kind represents a type node
195
* @param kind - Syntax kind to check
196
* @returns true if kind is a type node
197
*/
198
function isTypeNodeKind(kind: ts.SyntaxKind): boolean;
199
200
/**
201
* Check if syntax kind represents a JSDoc node
202
* @param kind - Syntax kind to check
203
* @returns true if kind is a JSDoc node
204
*/
205
function isJsDocKind(kind: ts.SyntaxKind): boolean;
206
207
/**
208
* Check if syntax kind represents a keyword
209
* @param kind - Syntax kind to check
210
* @returns true if kind is a keyword
211
*/
212
function isKeywordKind(kind: ts.SyntaxKind): boolean;
213
```
214
215
### AST Conversion
216
217
Utilities for converting AST to wrapped format for advanced analysis.
218
219
```typescript { .api }
220
/**
221
* Convert TypeScript AST to wrapped format with navigation aids
222
* @param sourceFile - Source file to convert
223
* @returns Converted AST with wrapped nodes and flat array
224
*/
225
function convertAst(sourceFile: ts.SourceFile): ConvertedAst;
226
227
/**
228
* Wrapped node with navigation properties
229
*/
230
interface NodeWrap {
231
node: ts.Node;
232
kind: ts.SyntaxKind;
233
children: NodeWrap[];
234
next?: NodeWrap;
235
skip?: NodeWrap;
236
parent?: NodeWrap;
237
}
238
239
/**
240
* Wrapped AST root node
241
*/
242
interface WrappedAst extends NodeWrap {
243
node: ts.SourceFile;
244
next: NodeWrap;
245
skip: undefined;
246
parent: undefined;
247
}
248
249
/**
250
* Result of AST conversion
251
*/
252
interface ConvertedAst {
253
wrapped: WrappedAst;
254
flat: ReadonlyArray<ts.Node>;
255
}
256
```
257
258
**Usage Examples:**
259
260
```typescript
261
import * as ts from "typescript";
262
import {
263
forEachToken,
264
forEachComment,
265
getPreviousToken,
266
getAstNodeAtPosition,
267
convertAst
268
} from "tsutils/util";
269
270
// Token iteration example
271
function analyzeTokens(sourceFile: ts.SourceFile) {
272
forEachToken(sourceFile, (token) => {
273
console.log(`Token: ${ts.SyntaxKind[token.kind]} at ${token.pos}-${token.end}`);
274
275
if (token.kind === ts.SyntaxKind.Identifier) {
276
const identifier = token as ts.Identifier;
277
console.log(` Identifier text: ${identifier.text}`);
278
}
279
});
280
}
281
282
// Comment analysis example
283
function analyzeComments(sourceFile: ts.SourceFile) {
284
forEachComment(sourceFile, (fullText, comment) => {
285
const commentText = fullText.substring(comment.pos, comment.end);
286
console.log(`Comment: ${commentText}`);
287
console.log(` Type: ${comment.kind === ts.SyntaxKind.SingleLineCommentTrivia ? 'single-line' : 'multi-line'}`);
288
});
289
}
290
291
// Navigation example
292
function analyzeNodeContext(node: ts.Node, sourceFile: ts.SourceFile) {
293
const previousToken = getPreviousToken(node, sourceFile);
294
const nextToken = getNextToken(node, sourceFile);
295
296
console.log(`Node: ${ts.SyntaxKind[node.kind]}`);
297
console.log(` Previous token: ${previousToken ? ts.SyntaxKind[previousToken.kind] : 'none'}`);
298
console.log(` Next token: ${nextToken ? ts.SyntaxKind[nextToken.kind] : 'none'}`);
299
}
300
301
// Position-based analysis example
302
function findNodeAtCursor(sourceFile: ts.SourceFile, cursorPosition: number) {
303
const nodeAtPosition = getAstNodeAtPosition(sourceFile, cursorPosition);
304
305
if (nodeAtPosition) {
306
console.log(`Node at cursor: ${ts.SyntaxKind[nodeAtPosition.kind]}`);
307
console.log(` Text: ${nodeAtPosition.getText(sourceFile)}`);
308
console.log(` Range: ${nodeAtPosition.pos}-${nodeAtPosition.end}`);
309
}
310
}
311
312
// AST conversion example
313
function analyzeWithWrappedAst(sourceFile: ts.SourceFile) {
314
const { wrapped, flat } = convertAst(sourceFile);
315
316
console.log(`Total nodes: ${flat.length}`);
317
318
// Navigate using wrapped structure
319
let current = wrapped.next;
320
while (current) {
321
console.log(`Node: ${ts.SyntaxKind[current.kind]}`);
322
current = current.next;
323
}
324
}
325
```