参考 babel 的 traverse 来实现, 使用到了访问器模式, 以及深度遍历
这章比较简单, 直接贴代码吧
const nodeTypes = require("./nodeTypes");
const { parser } = require("./parser");
function traverse(ast, visitor) {
function traverseArray(array, parent) {
array.forEach((child) => traverseNode(child, parent));
}
// 使用方式 转换 都是模拟的babel
function traverseNode(node, parent) {
let method = visitor[node.type];
// 当开始遍历子节点的时候,执行进入方法
if (method) {
if (typeof method === "function") {
method({ node }, parent);
} else {
method.enter({ node }, parent);
}
}
switch (node.type) {
case nodeTypes.Program:
traverseArray(node.body, node);
break;
case nodeTypes.ExpressionStatement:
traverseNode(node.expression, node);
break;
case nodeTypes.JSXElement:
traverseNode(node.openingElement, node);
traverseArray(node.children, node);
traverseNode(node.closingElement, node);
break;
case nodeTypes.openingElement:
traverseNode(node.name, node);
traverseArray(node.attributes, node);
break;
case nodeTypes.JSXIdentifier:
case nodeTypes.JSXText:
case nodeTypes.Literal:
break;
case nodeTypes.JSXAttribute:
traverseNode(node.name, node);
traverseNode(node.value, node);
break;
case nodeTypes.closingElement:
traverseNode(node.name, node);
break;
default:
break;
}
// 当遍历完子节点离开的时候
if (method && method.exit) {
method.exit({ node }, parent);
}
}
traverseNode(ast, null);
}
module.exports = {
parser,
};
let sourceCode = `<h1 id="title"><span>hello</span>world</h1>`;
let ast = parser(sourceCode);
traverse(ast, {
JSXOpeningElement: {
enter: (nodePath, parent) => {
console.log("进入开始", nodePath.node);
},
exit: (nodePath, parent) => {
console.log("离开开始", nodePath.node);
},
},
JSXClosingElement() {
console.log("进入JSXClosingElement");
},
});
进入开始 {
type: 'JSXOpeningElement',
name: { type: 'JSXIdentifier', name: 'h1' },
attributes: [ { type: 'JSXAttribute', name: [Object], value: [Object] } ]
}
离开开始 {
type: 'JSXOpeningElement',
name: { type: 'JSXIdentifier', name: 'h1' },
attributes: [ { type: 'JSXAttribute', name: [Object], value: [Object] } ]
}
进入开始 {
type: 'JSXOpeningElement',
name: { type: 'JSXIdentifier', name: 'span' },
attributes: []
}
离开开始 {
type: 'JSXOpeningElement',
name: { type: 'JSXIdentifier', name: 'span' },
attributes: []
}
进入JSXClosingElement
进入JSXClosingElement