or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-manipulation.mdcli-interface.mdcore-operations.mdindex.mdparser-configuration.mdsource-maps.md

ast-manipulation.mddocs/

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

```