silphius/app/astride/traverse.js
2024-06-22 11:43:52 -05:00

54 lines
1.6 KiB
JavaScript

export const TRAVERSAL_PATH = {
ArrayExpression: ['elements'],
ArrayPattern: ['elements'],
AssignmentExpression: ['left', 'right'],
AwaitExpression: ['argument'],
BinaryExpression: ['left', 'right'],
BlockStatement: ['body'],
CallExpression: ['arguments', 'callee'],
ConditionalExpression: ['alternate', 'consequent', 'test'],
DoWhileStatement: ['body', 'test'],
ExpressionStatement: ['expression'],
ForStatement: ['body', 'init', 'test', 'update'],
Identifier: [],
IfStatement: ['alternate', 'consequent', 'test'],
MemberExpression: ['object', 'property'],
Literal: [],
ObjectExpression: ['properties'],
ObjectPattern: ['properties'],
Program: ['body'],
Property: ['key', 'value'],
ReturnStatement: ['argument'],
SpreadElement: ['argument'],
UnaryExpression: ['argument'],
UpdateExpression: ['argument'],
VariableDeclaration: ['declarations'],
VariableDeclarator: ['id', 'init'],
WhileStatement: ['body', 'test'],
};
export default function traverse(node, visitor) {
/* v8 ignore next 3 */
if (!(node.type in TRAVERSAL_PATH)) {
throw new Error(`node type ${node.type} not traversable. (${Object.keys(node).join(', ')})`);
}
visitor(node, 'enter');
const path = TRAVERSAL_PATH[node.type];
let children;
if (path instanceof Function) {
children = path(node);
}
else if (Array.isArray(path)) {
children = [];
for (const key of path) {
children.push(...(Array.isArray(node[key]) ? node[key] : [node[key]]));
}
}
for (const child of children) {
if (child) {
traverse(child, visitor);
}
}
visitor(node, 'exit');
}