export const TRAVERSAL_PATH = { ArrayExpression: ['elements'], ArrayPattern: ['elements'], AssignmentExpression: ['left', 'right'], AwaitExpression: ['argument'], BinaryExpression: ['left', 'right'], BlockStatement: ['body'], BreakStatement: [], CallExpression: ['arguments', 'callee'], ChainExpression: ['expression'], ConditionalExpression: ['alternate', 'consequent', 'test'], DoWhileStatement: ['body', 'test'], ExpressionStatement: ['expression'], ForOfStatement: ['body', 'left', 'right'], ForStatement: ['body', 'init', 'test', 'update'], Identifier: [], IfStatement: ['alternate', 'consequent', 'test'], MemberExpression: ['object', 'property'], Literal: [], LogicalExpression: ['left', 'right'], 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'); for (const key of TRAVERSAL_PATH[node.type]) { if (Array.isArray(node[key])) { for (const child of node[key]) { if (child) { traverse(child, visitor); } } } else { if (node[key]) { traverse(node[key], visitor); } } } visitor(node, 'exit'); }