(给 前端大全加星标,提升前端技能 )
作者:前端e进阶 公号 / 南山皮小蛋 (本文来自作者投稿)
背景
AST 是非常有用的 。
上午一直在搬砖,下午听了小组一个老哥做的AST分享 ,觉得做的很好。
为了加深印象,就写了篇总结,顺便分享给大家,希望能给朋友们一些帮助和启发 。
可能小伙伴要问,AST真有这么厉害?
口说无凭, 且看几个具体的案例 。
且不说:
-
Vue => React -
React => Vue
的代码转换方法,
我们就看一个可以无痛升级旧版React 的工具:
react-codemod
代码地址:https://github.com/reactjs/react-codemod
这个工具,功能十分强大,使用起来也很方便,只需要运行一行命令:
npx react-codemod <transform> <path> [...options]
这些功能的实现,无不借助了AST 。
下面我们就进入今天的内容。
正文
本文的主要内容包括:
-
1 理论: AST
基本概念 -
2 实践: 使用 AST 实现一个代码转换工具, 把
var转换成let -
3 实践: 使用 AST 实现一个Eslint 插件,
禁用 console -
4 实践: 使用 AST 实现一个Babel插件,
过滤 Debugger
1. AST 基本概念
AST 是什么?
AST is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.
在计算机科学中,抽象语法 和抽象语法树 其实是源代码的抽象语法结构的树状表现形式 。
常用的浏览器就是通过将js代码转化为抽象语法树 来进行下一步的分析等其他操作。
所以将js转化为抽象语法树更利于程序的分析 。
AST 能做什么
- 代码语法的检查
- 代码风格的检查
- 代码的格式化
- 代码的高亮
- 代码错误提示
- 代码自动补全
- 等等。
AST 三板斧
- 生成AST
- 遍历和更新AST
- 将AST重新生成源码
为了便于理解, 我们看一个具体的例子。
这里顺便给大家介绍一个十分有用的网站:
https://astexplorer.net/
譬如:
声明, 变量, 类型等各种信息一应俱全。
而且这里也提供了各种插件模版供你选择:
十分的方便。
我们就根据这个例子, 我们做个小的实践 。
2. 实践: 使用AST实现一个代码转换工具, 把var转换成let
比如, 现在要重构一个老项目, 你要把项目里的var 全部替换成let , 你会怎么做?
手动替换? 或者借助工具一键替换?
现在就教你一招:一键替换大法 。
首先还是要介绍一把大杀器:
jscodeshift
它是一个 Javscript Codemod 工具,官方对 Codemod 的解释是:
Codemod is a tool/library to assist you with large-scale codebase refactors that can be partially automated but still require human oversight and occasional intervention.
jscodeshift 也是基于 esprima 的,其通过 path 可以很容易的在 AST 上遍历 node 。
现在我们就开始替换项目中的var.
首先,到 https://astexplorer.net 里面编写代码.
模板我们选:jscodeshift
官方自带的例子, 把变量名字反转:
我们现在要改变量, 这个工具很贴心的一点是可以高亮实时对照,
现在, 找到kind === var 的对象, 替换成let :
得到如下代码:
export default function transformer(file, api) { const j = api.jscodeshift; return j(file.source) .find(j.VariableDeclaration, { kind: 'var'}) .forEach(path => { const letStatement = j.variableDeclaration('let', path.node.declarations) j(path).replaceWith(letStatement) }) .toSource();}
这样也可以:
path.node.kind = 'let'; // 传入的实际是一个引用
实际效果:
大功告成!
假如我项目里有几个文件也需要相同的操作:
简单安装:
sudo npm install -g jscodeshift
执行:
jscodeshift -t transform.js ./src/demo.js --dry --print
这里用了--dry 和 --print
--dry 加上之后,不会立刻把新生成的代码覆盖源文件
--print 是打印出来看看
在实际项目里, 你需要在独立的分支里 操作,新生成代码之后, 需要你再检查检查 , review没有问题 之后才能合并。
3. 使用AST实现一个Eslint 插件, 禁用console
和上面的类似, 我们也可以做一个eslint 插件, 功能也很简单: 检查到使用console的时候就报错 。
期望达到的效果:
// Do not use console methods (at 1:9) console.log('haha')// --------^
我们这次选择 babel-eslint 模版。
代码实现:
const disallowMethods = ["log", "info", "warn", "error", "dir"];export default function(context) { return { Identifier(node) { const isConsoleMethod = disallowMethods.includes(node.name) && node.parent.type === "MemberExpression" && node.parent.object.name === "console"; if (!isConsoleMethod) return; context.report({ node, message: "Do not use console methods" }); } };}
实际效果:
简单有效。
不过你要是非要玩什么骚操作,比如自定义一个log, 那就没得搞了。
最后, 你可以把这段代码封装成一个完整的插件:
教你如何编写 Eslint 插件:
https://juejin.cn/post/6844903961804161031
你可以自行实践。
4. 使用AST实现一个Babel插件, 过滤debugger
最后一个是过滤源代码中的debugger, Transform 我们选择babelv7
这个插件,我们期望达到的效果是:
var a = 1debuggerfunction test() { debugger a++}debugger
到:
var a = 1;function test() { a++;}
这也是一个十分有用的功能。
代码实现:
export default function (babel) { const { types: t } = babel; return { name: "ast-transform", visitor: { DebuggerStatement(path) { path.remove() } } };}
实际效果:
符合预期。
总结
内容大概就是这么多,没什么难度,重在讲述理论和入门 。
对AST还不熟练的同学, 希望这篇可以帮助到你。
后面还有会AST在我们实际项目中的应用 , 我也会写一个实战篇 , 敬请期待!
以上。
推荐阅读 点击标题可跳转
觉得本文对你有帮助?请分享给更多人
关注「前端大全」加星标,提升前端技能
好文章,我在看❤️