AST 技巧:压缩变量定义

大家好,今天分享一种使用 AST 压缩变量定义的方法。

AST 预处理

一般情况下,拿到 JS 代码,我会用 AST 预处理插件进行处理,预处理插件会进行一些例如逗号表达式的还原、字面量的计算、十六进制还原等通用处理。但是有的时候预处理后的代码会将多个变量定义处理成每个变量单独定义一次,这样会使代码行数膨胀不少,不方便对比 AST 处理的结果是否正确,鼠标划到手软。

为了解决这个问题,写了一个使用 AST 将变量定义压缩的插件。

AST 压缩变量定义

image-20250604205521611

如果不处理的话,经常会遇到类似的代码,每个变量都定义一次,一下子产生几百行代码,每次都要划好久,比较麻烦。

image-20250604205621851

想要达到的效果如上,将所有的变量定义写到一起,这样只需要一行即可定义所有的变量,更方便查看代码。

插件思路

为了达到上面的效果,先要对比两种定义变量的方法在 AST 语法树中的区别。

image-20250604205836881

使用在线工具对比两种方法,发现同样都是 VariableDeclaration 节点,不同的是 declarations 属性中的节点数量不一样,如果单次定义多个变量,那么 declarations 属性中就会有多个节点,如果每次只定义一个变量,那么就会有很多个 VariableDeclaration 节点,每个节点的 declarations 属性中只有一个节点。

现在思路已经有了,接下来就剩写代码了,无非就是收集一下所有的变量定义节点,然后将其合并成一个即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//压缩变量定义
const mergeVar = {
VariableDeclaration(path) {
//如果不是单个变量定义则不处理
if (!(path.node.declarations.length === 1 && path.node.declarations[0].init === null)) {
return;
}
//保存结果
let result = [];
let p = path;
//当前节点用于保存其他变量
let next = p.getNextSibling();
while (true) {
//当前节点不要删除,直接获取下一个节点
next = p.getNextSibling();
//如果下个节点也是变量定义,则继续
if (next.isVariableDeclaration()) {
//将declarations属性中的内容保存起来,然后删除节点
result.push(...next.node.declarations);
next.remove();
} else {
//遇到第一个非变量定义节点,则退出
break;
}
//切换节点指向下一个节点
p = next;
}
//将上面收集到的所有节点添加到当前节点中
path.node.declarations.push(...result);
}
}
traverse(ast, mergeVar);

我的思路是保留第一个节点,然后将后续所有节点的内容添加到第一个节点的 declarations 属性中,当然这里也可以将所有节点都删除然后新建一个节点将收集到的所有内容添加进去。

总结

以上就是 AST 变量压缩插件的方法,代码很简单,思路也不复杂,大家可以尝试自己还原试试。

本文章首发于个人博客 LLLibra146’s blog

本文作者:LLLibra146

更多文章请关注公众号 (LLLibra146):LLLibra146

版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!

本文链接
https://blog.d77.xyz/archives/efeb326.html