babel的原理
- parse:把代码
code变成AST - traverse:遍历AST进行修改
- generate:把
AST变成代码code2
即 code -> ast -> ast2 -> code2
示例:手动把let变成var
import { parse } from "@babel/parser" // 把代码`code`变成`AST`import traverse from "@babel/traverse" // 遍历ASTimport generate from "@babel/generator" // 把`AST`变成代码`code2`const code = `let a = 'let'; let b = 2`const ast = parse(code, { sourceType: 'module' })traverse(ast, {enter: item => {if(item.node.type === 'VariableDeclaration'){if(item.node.kind === 'let'){item.node.kind = 'var'}}}})const result = generate(ast, {}, code)console.log(result.code)
在Chrome运行TS代码:node -r ts-node/register —inspect-brk xxx.ts
先打印看看ast是什么:
从ast对象的属性中,可以看到这个program就是上面code这段代码的程序,程序的body里有两个节点:
// const code = `let a = 'let'; let b = 2`let a = 'let' // 节点1let b = 2 // 节点2
再看一下这些节点都包含哪些属性,重点关注这三个属性
...// 声明的节点信息declarations: [Node]// 变量声明的关键字是letkind: "let"// 类型,说明是一个变量声明type: "VariableDeclaration"...// declarations是一个节点数组,因为可以同时声明多个节点// let a = 'let'// name为a的标识符,对应初始值value是'let',类型是字符串declarations: [0: Node{id: {...,name: "a",type: "Identifier"},init: {...,value: "let",type: "StringLiteral"}},length: 1]
所以说AST就是用来将源代码字符串,表示成一个树形结构
// traverse用来遍历AST,可以对AST进行修改traverse(ast, {// 每进入一个节点,就执行item=>{}函数enter: item => {...}})
这样看来,将let变成var就水到渠成了
traverse(ast, {enter: item => {if(item.node.type === 'VariableDeclaration'){ // 如果是变量声明if(item.node.kind === 'let'){ // 如果是let声明item.node.kind = 'var' // 将let改成var}}}})
再使用generate函数将新的AST翻译成code
const result = generate(ast, {}, code) // 传入修改后的ast,还有原始codeconsole.log(result.code) // 得到新的code
运行后就得到了
var a = 'let';
var b = 2;
为什么要用AST
- 你很难用正则表达式去替换,正则很容易把
let a = 'let'变成var a = 'var' - 你需要识别每个单词的意思,才能做到只修改用于变量声明的
let - 而AST能明确告诉你每个
let的意思
