Tools for walking and analyzing AST structures with flexible visitor patterns and control flow mechanisms.
Main class for traversing AST nodes with enter/leave callbacks and flexible visitor key configuration.
/**
* A simple traverser class for walking AST trees
*/
class SimpleTraverser {
/**
* Traverse AST tree with callbacks (static method)
* @param node - Root AST node to traverse
* @param options - Traversal configuration with callbacks
*/
static traverse(node: ESNode, options: TraverserOptions): void;
/**
* Traverse AST tree with callbacks (instance method)
* @param node - Root AST node to traverse
* @param options - Traversal configuration with callbacks
*/
traverse(node: ESNode, options: TraverserOptions): void;
/** Error constant for skipping subtree traversal */
static Skip: Error;
/** Error constant for stopping traversal completely */
static Break: Error;
}
interface TraverserOptions {
/** Callback invoked when entering each node */
enter: (node: ESNode, parent?: ESNode) => void;
/** Callback invoked when leaving each node */
leave: (node: ESNode, parent?: ESNode) => void;
/** Custom visitor keys for traversal (optional) */
visitorKeys?: VisitorKeysType;
}
type TraverserCallback = (node: ESNode, parent?: ESNode) => void;Usage Examples:
import { SimpleTraverser } from "hermes-parser";
const ast = parse("function add(a, b) { return a + b; }");
// Basic traversal with enter/leave callbacks
SimpleTraverser.traverse(ast, {
enter(node, parent) {
console.log(`Entering ${node.type}`);
if (parent) {
console.log(` Parent: ${parent.type}`);
}
},
leave(node, parent) {
console.log(`Leaving ${node.type}`);
}
});
// Collect all function names
const functionNames = [];
SimpleTraverser.traverse(ast, {
enter(node) {
if (node.type === "FunctionDeclaration" && node.id) {
functionNames.push(node.id.name);
}
},
leave() {} // Required but can be empty
});
// Instance-based traversal
const traverser = new SimpleTraverser();
traverser.traverse(ast, {
enter: (node) => console.log(node.type),
leave: () => {}
});Control traversal flow using error-based mechanisms for skipping and stopping.
/** Skip traversal of current node's children */
const SimpleTraverserSkip: Error;
/** Stop traversal entirely */
const SimpleTraverserBreak: Error;Control Flow Examples:
SimpleTraverser.traverse(ast, {
enter(node) {
// Skip function bodies to only analyze declarations
if (node.type === "BlockStatement") {
throw SimpleTraverser.Skip;
}
// Stop traversal when finding specific pattern
if (node.type === "CallExpression" &&
node.callee.name === "stopHere") {
throw SimpleTraverser.Break;
}
console.log(`Processing ${node.type}`);
},
leave() {}
});
// Alternative: Use static properties
SimpleTraverser.traverse(ast, {
enter(node) {
if (node.type === "FunctionExpression") {
throw SimpleTraverser.Skip; // Skip function body
}
},
leave() {}
});Functions for managing and using visitor keys that define which properties to traverse on each node type.
/**
* Get visitor keys for a specific AST node type
* @param node - AST node to get keys for
* @param visitorKeys - Optional custom visitor keys mapping
* @returns Array of property names to traverse
*/
function getVisitorKeys(node: ESNode, visitorKeys?: VisitorKeysType): ReadonlyArray<string>;
/**
* Check if a value is an AST node
* @param value - Value to test
* @returns True if value is an AST node object
*/
function isNode(value: unknown): value is ESNode;
/** Default Flow ESTree visitor keys */
const FlowVisitorKeys: VisitorKeysType;
type VisitorKeysType = {
[nodeType: string]: ReadonlyArray<string>;
};Visitor Keys Examples:
import { getVisitorKeys, isNode, FlowVisitorKeys } from "hermes-parser";
// Get visitor keys for a node
const functionNode = { type: "FunctionDeclaration", id: null, params: [], body: null };
const keys = getVisitorKeys(functionNode);
console.log(keys); // ["id", "params", "body", "typeParameters", "returnType", "predicate"]
// Check if value is AST node
console.log(isNode({ type: "Identifier", name: "x" })); // true
console.log(isNode("not a node")); // false
// Use custom visitor keys
const customKeys = {
...FlowVisitorKeys,
CustomNode: ["customProp1", "customProp2"]
};
SimpleTraverser.traverse(ast, {
enter(node) {
const keys = getVisitorKeys(node, customKeys);
console.log(`${node.type} keys:`, keys);
},
leave() {},
visitorKeys: customKeys
});Common patterns for AST analysis and manipulation.
Finding Specific Nodes:
function findNodes(ast, predicate) {
const results = [];
SimpleTraverser.traverse(ast, {
enter(node) {
if (predicate(node)) {
results.push(node);
}
},
leave() {}
});
return results;
}
// Find all variable declarations
const variables = findNodes(ast, node =>
node.type === "VariableDeclarator"
);
// Find all function calls
const calls = findNodes(ast, node =>
node.type === "CallExpression"
);Collecting Node Statistics:
function analyzeAST(ast) {
const stats = {};
SimpleTraverser.traverse(ast, {
enter(node) {
stats[node.type] = (stats[node.type] || 0) + 1;
},
leave() {}
});
return stats;
}
const analysis = analyzeAST(ast);
console.log(analysis);
// { Program: 1, FunctionDeclaration: 2, Identifier: 8, ... }Parent-Child Relationship Analysis:
function buildParentMap(ast) {
const parentMap = new WeakMap();
SimpleTraverser.traverse(ast, {
enter(node, parent) {
if (parent) {
parentMap.set(node, parent);
}
},
leave() {}
});
return parentMap;
}
const parents = buildParentMap(ast);
// Later: const parent = parents.get(someNode);When errors occur during traversal, contextual information is automatically attached.
interface TraversalError extends Error {
currentNode?: {
type: string;
range?: [number, number];
loc?: SourceLocation;
};
}Error Handling:
SimpleTraverser.traverse(ast, {
enter(node) {
if (node.type === "CallExpression") {
// This error will include currentNode context
throw new Error("Processing failed");
}
},
leave() {}
});