A JavaScript parser built from the Hermes engine that supports ES6, Flow, and JSX syntax
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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() {}
});