65 lines
1.9 KiB
JavaScript
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');
|
|
}
|