# 如何写 babel 插件
# 认识 babel
大家知道 babel
是一款语法转换器,有了它我们才可以把es6
的代码转换成低版本的 js
从而兼容低版本的浏览器。但借助babel
能做到的事情远不止这些,比如我认为react
最有难度的jsx
解析,就是通过babel
处理的。
# babel api
babel 官网 (opens new window)我们可以看到提供了:
- @babel/parser 可以把代码解析成 ast
- @babel/generator 把 ast 转换成代码
- @babel/traverse 遍历/修改 ast
- @babel/types 该包含手动构建 AST 和检查 AST 节点类型的方法
- @babel/core 几乎整合了所有 api
这里直接使用@babel/core
:
import babel from "@babel/core"
const code = `
const a = 1
const x = 2
`;
// step 1 解析得到 ast
const ast = babel.parse(code,{
plugins:[], // jsx typescript 等等解析代码需要的插件
sourceType:'module' // 解析模式 可以是 script 脚本 module 模块 或者 unambiguous 让 babel 自己去猜是什么
})
// step 2 修改
// path.node 即遍历到的所有 ast 节点
// 这个 path 的原型上提供了一系列的检测方法 可以通过查看源码 或者 debugger 上点开 __proto__ 查阅
babel.traverse(ast, {
enter(path) {
if (path.isIdentifier({ name: "a" })) { // 是否是一个 Identifier 而且 name 是 a
path.node.name = "b";
}
if(path.isVariableDeclaration({kind:'const',start:5})){ // 是否是一个 VariableDeclaration 而且 kind 是 const 节点开始与字符串第5个字符
path.node.kind = 'let'
}
},
});
// step3 ast 转换为 code
babel.transformFromAst(ast,code,{},function(err,res){
const { code, map, ast } = res
console.log(code) // `let b = 1;const x = 2;` // 修改成功
})
其中 babel.traverse
也可以改写为:
babel.traverse(ast, {
// enter(path) {
// if (path.isIdentifier({ name: "a" })) {
// path.node.name = "b";
// }
// if(path.isVariableDeclaration({kind:'const',start:5})){
// path.node.kind = 'let'
// }
// },
Identifier(path){
if(path.node.name==='a'){
path.node.name = "b";
}
},
VariableDeclaration(path){
if(path.node.kind==='const' && path.node.start === 5){
path.node.kind = 'let'
}
}
});
关于 path
以及下面插件用到的 vistor
babel 手册 (opens new window)有非常详细的介绍
# babel 插件示例
// package.json
{
"scripts": {
"build": "babel src -d dist",
"debug": "node --inspect node_modules/@babel/cli/bin/babel src -d dist",
},
"devDependencies": {
"@babel/cli": "^7.17.6",
"@babel/core": "^7.17.9",
}
}
// .babelrc
"plugins": ["./plugin/babel-plugin-test"]
// plugin/babel-plugin-test.js
module.exports = (babel) => {
return {
visitor: {
Identifier(path, state) {
if(path.node.name==='a'){
path.node.name = "b";
}
},
VariableDeclaration(path,state){
if(path.node.kind==='const'){
path.node.kind = 'let'
}
}
}
}
};
// src/index.js
const a = 1
// then yarn build