关注它,不迷路。
本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!
1. 需求分析
如题,如何对多个节点使用同一插件?例如我有下面一行代码:
var a = 1+2,b = ![];
如果使用常规的方法,你需要这样写插件:
const visitor = {
"BinaryExpression"(path)
{
const {confident,value} = path.evaluate();
confident && path.replaceWith(types.valueToNode(value));
},
"UnaryExpression"(path)
{
const {confident,value} = path.evaluate();
confident && path.replaceWith(types.valueToNode(value));
},
}
可以看到,这两个代码是一模一样的,那能不能将其合并起来?
2. 混淆代码
还原前:
var a = 1+2,b = ![];
还原后:
var a = 3,b = false;
条件 : 写一个插件,需要同时兼顾这两种不同类型的节点。
3. 思路
还是得从源码入手,定位到 traverse 函数:
function traverse(parent, opts = {}, scope, state, parentPath) {
if (!parent) return;
if (!opts.noScope && !scope) {
if (parent.type !== "Program" && parent.type !== "File") {
throw new Error("You must pass a scope and parentPath unless traversing a Program/File. " + `Instead of that you tried to traverse a ${parent.type} node without ` + "passing scope and parentPath.");
}
}
if (!VISITOR_KEYS[parent.type]) {
return;
}
visitors.explode(opts);
(0, _traverseNode.traverseNode)(parent, opts, scope, state, parentPath);
}
看到 这行代码:
visitors.explode(opts);
跟进去:
function explode(visitor) {
if (visitor._exploded) return visitor;
visitor._exploded = true;
for (const nodeType of Object.keys(visitor)) {
if (shouldIgnoreKey(nodeType)) continue;
const parts = nodeType.split("|");
if (parts.length === 1) continue;
const fns = visitor[nodeType];
delete visitor[nodeType];
for (const part of parts) {
visitor[part] = fns;
}
}
......
}
定位到for循环,可以看到这行代码:
const parts = nodeType.split("|");
这里是将 nodeType 进行了分割,而 nodeType 来自于 Object.keys(visitor),因此可以尝试在插件中key 用 "|" 隔开试试。
4. 插件源码
const visitor = {
"BinaryExpression|UnaryExpression"(path)
{
const {confident,value} = path.evaluate();
confident && path.replaceInline(t.valueToNode(value));
},
这里需要注意的是,key一定要是字符串形式,否则会报错。
今天的文章就分享到这里,后续分享更多的技巧,敬请期待。
欢迎加入知识星球,学习更多AST和爬虫技巧。