0
# AST Manipulation
1
2
Tools for traversing and modifying Abstract Syntax Trees with type safety using the ast-types library integration.
3
4
## Capabilities
5
6
### Visit Function
7
8
Traverse and potentially modify an abstract syntax tree using a convenient visitor syntax.
9
10
```typescript { .api }
11
/**
12
* Traverse and modify AST nodes using visitor pattern
13
* @param ast - The AST node to traverse
14
* @param visitor - Visitor object with node-specific methods
15
* @returns The modified AST
16
*/
17
function visit(ast: types.ASTNode, visitor: Visitor): types.ASTNode;
18
19
interface Visitor {
20
[methodName: string]: (path: NodePath) => any;
21
}
22
23
interface NodePath {
24
/** The current AST node */
25
value: any;
26
/** Parent path in the traversal */
27
parent: NodePath | null;
28
/** Name of the property this node represents */
29
name: string | number | null;
30
/** Continue traversing child nodes */
31
traverse(path?: NodePath): void;
32
/** Replace current node with new node */
33
replace(node: any): void;
34
/** Insert nodes before current node */
35
insertBefore(...nodes: any[]): void;
36
/** Insert nodes after current node */
37
insertAfter(...nodes: any[]): void;
38
/** Remove current node */
39
prune(): void;
40
}
41
```
42
43
**Usage Examples:**
44
45
```typescript
46
import { parse, visit, print } from "recast";
47
48
const ast = parse(sourceCode);
49
50
// Transform all identifier names to uppercase
51
visit(ast, {
52
visitIdentifier(path) {
53
const node = path.value;
54
node.name = node.name.toUpperCase();
55
this.traverse(path);
56
}
57
});
58
59
// Replace function declarations with arrow functions
60
visit(ast, {
61
visitFunctionDeclaration(path) {
62
const node = path.value;
63
const arrowFunction = types.builders.variableDeclaration("const", [
64
types.builders.variableDeclarator(
65
node.id,
66
types.builders.arrowFunctionExpression(node.params, node.body)
67
)
68
]);
69
path.replace(arrowFunction);
70
return false; // Skip traversing children
71
}
72
});
73
```
74
75
### AST Types Integration
76
77
Access to the complete ast-types library for AST construction and validation.
78
79
```typescript { .api }
80
/**
81
* AST types namespace providing builders and validators
82
*/
83
namespace types {
84
/** AST node constructor functions */
85
const builders: Builders;
86
/** Type checking and assertion functions */
87
const namedTypes: NamedTypes;
88
/** Built-in type validators */
89
const builtInTypes: BuiltInTypes;
90
}
91
92
interface Builders {
93
/** Create identifier node */
94
identifier(name: string): Identifier;
95
/** Create literal node */
96
literal(value: any): Literal;
97
/** Create function expression */
98
functionExpression(
99
id: Identifier | null,
100
params: Pattern[],
101
body: BlockStatement
102
): FunctionExpression;
103
/** Create variable declaration */
104
variableDeclaration(
105
kind: "var" | "let" | "const",
106
declarations: VariableDeclarator[]
107
): VariableDeclaration;
108
/** Create variable declarator */
109
variableDeclarator(
110
id: Pattern,
111
init?: Expression | null
112
): VariableDeclarator;
113
/** Create arrow function expression */
114
arrowFunctionExpression(
115
params: Pattern[],
116
body: BlockStatement | Expression
117
): ArrowFunctionExpression;
118
/** Create member expression */
119
memberExpression(
120
object: Expression,
121
property: Expression | Identifier,
122
computed?: boolean
123
): MemberExpression;
124
/** Create call expression */
125
callExpression(
126
callee: Expression,
127
arguments: Array<Expression | SpreadElement>
128
): CallExpression;
129
// ... many more builder functions
130
}
131
132
interface NamedTypes {
133
/** Assert node is an Identifier */
134
Identifier: TypeChecker<Identifier>;
135
/** Assert node is a FunctionDeclaration */
136
FunctionDeclaration: TypeChecker<FunctionDeclaration>;
137
/** Assert node is a VariableDeclaration */
138
VariableDeclaration: TypeChecker<VariableDeclaration>;
139
// ... many more type checkers
140
}
141
142
interface TypeChecker<T> {
143
/** Check if value is of this type */
144
check(value: any): value is T;
145
/** Assert value is of this type (throws if not) */
146
assert(value: any): asserts value is T;
147
/** Get the type definition */
148
def: TypeDefinition;
149
}
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
import { parse, types, visit, print } from "recast";
156
157
const b = types.builders;
158
const n = types.namedTypes;
159
160
const ast = parse("function add(a, b) { return a + b; }");
161
162
visit(ast, {
163
visitFunctionDeclaration(path) {
164
const node = path.value;
165
166
// Type checking
167
if (n.FunctionDeclaration.check(node)) {
168
// Convert to arrow function
169
const arrowFunc = b.variableDeclaration("const", [
170
b.variableDeclarator(
171
node.id,
172
b.arrowFunctionExpression(node.params, node.body)
173
)
174
]);
175
path.replace(arrowFunc);
176
}
177
return false;
178
}
179
});
180
```
181
182
### Path-based Traversal
183
184
Advanced path manipulation for complex AST transformations.
185
186
```typescript { .api }
187
interface NodePath {
188
/** Get the value at this path */
189
value: any;
190
/** Get parent path */
191
parent: NodePath | null;
192
/** Get property name/index */
193
name: string | number | null;
194
/** Get scope information */
195
scope: Scope;
196
197
/** Navigate to parent path */
198
parentPath: NodePath | null;
199
/** Get child paths */
200
get(...names: Array<string | number>): NodePath;
201
/** Check if path exists */
202
has(...names: Array<string | number>): boolean;
203
204
/** Replace node at this path */
205
replace(node: any): void;
206
/** Insert nodes before current node */
207
insertBefore(...nodes: any[]): void;
208
/** Insert nodes after current node */
209
insertAfter(...nodes: any[]): void;
210
/** Remove node at this path */
211
prune(): void;
212
213
/** Continue traversal */
214
traverse(path?: NodePath): void;
215
}
216
```
217
218
**Usage Examples:**
219
220
```typescript
221
import { parse, visit } from "recast";
222
223
visit(ast, {
224
visitCallExpression(path) {
225
const node = path.value;
226
227
// Access function name through path navigation
228
const calleePath = path.get("callee");
229
if (calleePath.value.name === "console") {
230
const propertyPath = path.get("property");
231
if (propertyPath.value.name === "log") {
232
// Replace console.log with console.warn
233
propertyPath.replace(types.builders.identifier("warn"));
234
}
235
}
236
237
this.traverse(path);
238
}
239
});
240
```
241
242
### Scope Analysis
243
244
Access to scope information for variable binding analysis.
245
246
```typescript { .api }
247
interface Scope {
248
/** Parent scope */
249
parent: Scope | null;
250
/** Declared bindings in this scope */
251
bindings: { [name: string]: Binding[] };
252
/** Scope type */
253
type: "function" | "block" | "catch" | "with";
254
255
/** Check if identifier is bound in this scope */
256
declares(name: string): boolean;
257
/** Look up binding in scope chain */
258
lookup(name: string): Scope | null;
259
/** Get all names declared in this scope */
260
getBindingNames(): string[];
261
}
262
263
interface Binding {
264
/** Identifier node that declares this binding */
265
identifier: Identifier;
266
/** Scope where this binding is declared */
267
scope: Scope;
268
/** AST path to the binding */
269
path: NodePath;
270
}
271
```
272
273
**Usage Examples:**
274
275
```typescript
276
import { parse, visit } from "recast";
277
278
visit(ast, {
279
visitIdentifier(path) {
280
const node = path.value;
281
const scope = path.scope;
282
283
// Check if identifier is bound in current scope
284
if (scope.declares(node.name)) {
285
console.log(`${node.name} is declared locally`);
286
} else {
287
console.log(`${node.name} is from outer scope`);
288
}
289
290
this.traverse(path);
291
}
292
});
293
```
294
295
## Common AST Transformation Patterns
296
297
### Function Conversion
298
299
Converting between different function syntaxes.
300
301
```typescript
302
// Function declaration to arrow function
303
visit(ast, {
304
visitFunctionDeclaration(path) {
305
const node = path.value;
306
const arrow = types.builders.variableDeclaration("const", [
307
types.builders.variableDeclarator(
308
node.id,
309
types.builders.arrowFunctionExpression(node.params, node.body)
310
)
311
]);
312
path.replace(arrow);
313
return false;
314
}
315
});
316
```
317
318
### Variable Renaming
319
320
Systematically renaming variables throughout the AST.
321
322
```typescript
323
const renameMap = { oldName: "newName" };
324
325
visit(ast, {
326
visitIdentifier(path) {
327
const node = path.value;
328
if (renameMap[node.name]) {
329
node.name = renameMap[node.name];
330
}
331
this.traverse(path);
332
}
333
});
334
```
335
336
### Code Injection
337
338
Adding new statements or expressions to existing code.
339
340
```typescript
341
visit(ast, {
342
visitFunctionDeclaration(path) {
343
const node = path.value;
344
345
// Add console.log at start of function
346
const logStatement = types.builders.expressionStatement(
347
types.builders.callExpression(
348
types.builders.memberExpression(
349
types.builders.identifier("console"),
350
types.builders.identifier("log")
351
),
352
[types.builders.literal(`Entering function ${node.id.name}`)]
353
)
354
);
355
356
node.body.body.unshift(logStatement);
357
this.traverse(path);
358
}
359
});
360
```