Acorn Walk is an ECMAScript (ESTree) AST walker library that provides multiple traversal strategies for JavaScript syntax trees. It offers a comprehensive set of algorithms for visiting, finding, and processing AST nodes with flexible state management and custom visitor functions.
npm install acorn-walkimport { simple, ancestor, recursive, full, findNodeAt, base } from "acorn-walk";For CommonJS:
const { simple, ancestor, recursive, full, findNodeAt, base } = require("acorn-walk");import { parse } from "acorn";
import { simple, findNodeAt } from "acorn-walk";
// Parse JavaScript code into AST
const ast = parse("let x = 10; function foo() { return x + 1; }");
// Simple walk to find all literals
simple(ast, {
Literal(node) {
console.log(`Found literal: ${node.value}`);
}
});
// Find specific node at position
const node = findNodeAt(ast, 4, 10, "Literal");
if (node) {
console.log(`Found node: ${node.node.value}`);
}Acorn Walk is built around several key components:
Performs a basic walk over an AST, calling visitor functions for specific node types.
/**
* Performs a simple walk over a tree with visitor callbacks
* @param node - The AST node to walk
* @param visitors - Object mapping node types to visitor functions
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @param override - Optional node type override
*/
function simple(
node: Node,
visitors: SimpleVisitors,
baseVisitor?: RecursiveVisitors,
state?: any,
override?: string
): void;
interface SimpleVisitors {
[nodeType: string]: (node: Node, state: any) => void;
}Usage Example:
import { parse } from "acorn";
import { simple } from "acorn-walk";
const ast = parse("let x = 42; console.log(x);");
simple(ast, {
VariableDeclaration(node, state) {
console.log("Found variable declaration");
},
CallExpression(node, state) {
console.log("Found function call");
}
});Walks an AST while building up an array of ancestor nodes, useful for context-aware processing.
/**
* Walks a tree building up an array of ancestor nodes
* @param node - The AST node to walk
* @param visitors - Object mapping node types to visitor functions
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @param override - Optional node type override
*/
function ancestor(
node: Node,
visitors: AncestorVisitors,
baseVisitor?: RecursiveVisitors,
state?: any,
override?: string
): void;
interface AncestorVisitors {
[nodeType: string]: (node: Node, state: any, ancestors: Node[]) => void;
}Usage Example:
import { parse } from "acorn";
import { ancestor } from "acorn-walk";
const ast = parse("function outer() { function inner() { return 42; } }");
ancestor(ast, {
FunctionDeclaration(node, state, ancestors) {
console.log(`Function ${node.id.name} has ${ancestors.length - 1} ancestors`);
}
});Provides full control over the walking process, where visitor functions are responsible for continuing traversal.
/**
* Performs a recursive walk where visitors control child traversal
* @param node - The AST node to walk
* @param state - Initial state value
* @param functions - Object mapping node types to walker functions
* @param baseVisitor - Optional fallback walker functions
* @param override - Optional node type override
*/
function recursive(
node: Node,
state: any,
functions: RecursiveVisitors,
baseVisitor?: RecursiveVisitors,
override?: string
): void;
interface RecursiveVisitors {
[nodeType: string]: (node: Node, state: any, callback: WalkerCallback) => void;
}
interface WalkerCallback {
(node: Node, state: any): void;
}Usage Example:
import { parse } from "acorn";
import { recursive } from "acorn-walk";
const ast = parse("let x = { a: 1, b: 2 };");
recursive(ast, { depth: 0 }, {
VariableDeclaration(node, state, c) {
console.log(`Variable declaration at depth ${state.depth}`);
for (const decl of node.declarations) {
c(decl, { ...state, depth: state.depth + 1 });
}
}
});Triggers a callback on every node in the AST.
/**
* Performs a full walk calling callback on each node
* @param node - The AST node to walk
* @param callback - Function called for each node
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @param override - Optional node type override
*/
function full(
node: Node,
callback: FullWalkerCallback,
baseVisitor?: RecursiveVisitors,
state?: any,
override?: string
): void;
interface FullWalkerCallback {
(node: Node, state: any, type: string): void;
}Usage Example:
import { parse } from "acorn";
import { full } from "acorn-walk";
const ast = parse("let x = 10;");
let nodeCount = 0;
full(ast, (node, state, type) => {
nodeCount++;
console.log(`Node ${nodeCount}: ${type}`);
});Combines full walking with ancestor tracking.
/**
* Performs a full walk with ancestor array on each node
* @param node - The AST node to walk
* @param callback - Function called for each node with ancestors
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
*/
function fullAncestor(
node: Node,
callback: FullAncestorWalkerCallback,
baseVisitor?: RecursiveVisitors,
state?: any
): void;
interface FullAncestorWalkerCallback {
(node: Node, state: any, ancestors: Node[], type: string): void;
}Locates a node at specific start/end positions.
/**
* Finds a node at given start and/or end offsets
* @param node - The AST node to search
* @param start - Start position (null as wildcard)
* @param end - End position (null as wildcard)
* @param test - Node type string or predicate function
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @returns Found node and state, or undefined
*/
function findNodeAt(
node: Node,
start: number | null,
end?: number | null,
test?: string | FindPredicate,
baseVisitor?: RecursiveVisitors,
state?: any
): Found | undefined;
interface Found {
node: Node;
state: any;
}
interface FindPredicate {
(type: string, node: Node): boolean;
}Usage Example:
import { parse } from "acorn";
import { findNodeAt } from "acorn-walk";
const code = "let x = 42;";
const ast = parse(code);
// Find literal at specific position
const result = findNodeAt(ast, 8, 10, "Literal");
if (result) {
console.log(`Found literal: ${result.node.value}`);
}Finds the innermost node that contains a given position.
/**
* Finds the innermost node containing the given position
* @param node - The AST node to search
* @param pos - Position to search around
* @param test - Node type string or predicate function
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @returns Found node and state, or undefined
*/
function findNodeAround(
node: Node,
pos: number,
test?: string | FindPredicate,
baseVisitor?: RecursiveVisitors,
state?: any
): Found | undefined;Finds the outermost matching node after a given position.
/**
* Finds the outermost matching node after a given position
* @param node - The AST node to search
* @param pos - Position to search after
* @param test - Node type string or predicate function
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @returns Found node and state, or undefined
*/
function findNodeAfter<TState>(
node: Node,
pos: number,
test?: string | FindPredicate,
baseVisitor?: RecursiveVisitors<TState>,
state?: TState
): Found<TState> | undefined;Finds the outermost matching node before a given position.
/**
* Finds the outermost matching node before a given position
* @param node - The AST node to search
* @param pos - Position to search before
* @param test - Node type string or predicate function
* @param baseVisitor - Optional custom walker algorithm
* @param state - Optional initial state
* @returns Found node and state, or undefined
*/
function findNodeBefore<TState>(
node: Node,
pos: number,
test?: string | FindPredicate,
baseVisitor?: RecursiveVisitors<TState>,
state?: TState
): Found<TState> | undefined;Creates custom walker objects by combining visitor functions with defaults.
/**
* Creates a custom walker by combining functions with base defaults
* @param functions - Object mapping node types to walker functions
* @param baseVisitor - Optional base walker to inherit from
* @returns New walker object with combined functions
*/
function make<TState>(
functions: RecursiveVisitors<TState>,
baseVisitor?: RecursiveVisitors<TState>
): RecursiveVisitors<TState>;Usage Example:
import { make, base } from "acorn-walk";
const customWalker = make({
FunctionDeclaration(node, state, c) {
console.log(`Custom function handler for ${node.id.name}`);
// Call default behavior
base.FunctionDeclaration(node, state, c);
}
}, base);Default walker implementation containing visitor functions for all ESTree node types.
/**
* Default walker object with visitors for all ESTree node types
*/
const base: RecursiveVisitors<any>;The base object contains walker functions for all standard ESTree node types including:
interface Node {
type: string;
start?: number;
end?: number;
[key: string]: any;
}
// Aggregate node types for convenience
type AggregateType = {
Expression: Expression;
Statement: Statement;
Function: Function;
Class: Class;
Pattern: Pattern;
ForInit: VariableDeclaration | Expression;
};
interface SimpleVisitors<TState> {
[nodeType: string]: (node: Node, state: TState) => void;
// Also supports aggregate types:
Expression?: (node: Expression, state: TState) => void;
Statement?: (node: Statement, state: TState) => void;
Function?: (node: Function, state: TState) => void;
Class?: (node: Class, state: TState) => void;
Pattern?: (node: Pattern, state: TState) => void;
}
interface AncestorVisitors<TState> {
[nodeType: string]: (node: Node, state: TState, ancestors: Node[]) => void;
}
interface RecursiveVisitors<TState> {
[nodeType: string]: (node: Node, state: TState, callback: WalkerCallback<TState>) => void;
}
interface WalkerCallback<TState> {
(node: Node, state: TState): void;
}
interface FullWalkerCallback<TState> {
(node: Node, state: TState, type: string): void;
}
interface FullAncestorWalkerCallback<TState> {
(node: Node, state: TState, ancestors: Node[], type: string): void;
}
interface FindPredicate {
(type: string, node: Node): boolean;
}
interface Found<TState> {
node: Node;
state: TState;
}import { parse } from "acorn";
import { simple } from "acorn-walk";
const ast = parse("function foo() { let x = 1; return x + 1; }");
const info = { functions: [], variables: [] };
simple(ast, {
FunctionDeclaration(node, state) {
state.functions.push(node.id.name);
},
VariableDeclaration(node, state) {
node.declarations.forEach(decl => {
state.variables.push(decl.id.name);
});
}
}, null, info);
console.log(info); // { functions: ['foo'], variables: ['x'] }import { parse } from "acorn";
import { ancestor } from "acorn-walk";
const ast = parse("function outer() { function inner() { console.log('nested'); } }");
ancestor(ast, {
CallExpression(node, state, ancestors) {
const depth = ancestors.filter(n => n.type === 'FunctionDeclaration').length;
console.log(`Function call at nesting depth: ${depth}`);
}
});import { parse } from "acorn";
import { findNodeAround } from "acorn-walk";
const code = "let x = function() { return 42; };";
const ast = parse(code, { locations: true });
// Find what's at position 20 (inside the function)
const result = findNodeAround(ast, 20);
if (result) {
console.log(`Node at position 20: ${result.node.type}`);
}