silphius/app/astride/traverse.js
2024-06-22 08:02:23 -05:00

65 lines
1.9 KiB
JavaScript

export const TRAVERSAL_PATH = {
ArrayExpression: (node) => node.elements.map(({expression}) => expression),
ArrayPattern: ['elements'],
AssignmentExpression: ['left', 'right'],
AssignmentPatternProperty: ['key'],
AwaitExpression: ['argument'],
BinaryExpression: ['left', 'right'],
BlockStatement: ['stmts'],
BooleanLiteral: [],
CallExpression: (node) => ([
node.callee,
...node.arguments.map(({expression}) => expression),
]),
Computed: ['expression'],
ConditionalExpression: ['alternate', 'consequent', 'test'],
DoWhileStatement: ['body', 'test'],
ExpressionStatement: ['expression'],
ForStatement: ['body', 'init', 'test', 'update'],
Identifier: [],
IfStatement: ['alternate', 'consequent', 'test'],
KeyValuePatternProperty: ['key', 'value'],
KeyValueProperty: ['key', 'value'],
MemberExpression: ['object', 'property'],
Module: ['body'],
NullLiteral: [],
NumericLiteral: [],
ObjectExpression: ['properties'],
ObjectPattern: ['properties'],
OptionalChainingExpression: ['base'],
ParenthesisExpression: ['expression'],
RegExpLiteral: [],
ReturnStatement: ['argument'],
StringLiteral: [],
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`);
}
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');
}