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'], 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'); }