54 lines
1.6 KiB
JavaScript
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');
|
|
}
|