利用 babel 操作 AST 语法树

AST

# 将代码转为 ast 语法树

  1. 安装npm i webpack webpack-cli
  2. webpack里面用到了,自己写插件也会用到。
  3. webpack.config.js
const path = require('path')
module.exports = {
    devtool:'none',
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve('dist')
    }
}
1
2
3
4
5
6
7
8
9
10
  1. 安装npm i @babel/parser
  2. index.js中引用
  const parser = require('@babel/parser')

  let code = `let name = 'xm'`

  // 利用 parse 接收一个 JS 代码片段,可以将它转为 ast 
  let ast = parser.parse(code)
  console.log(ast)
1
2
3
4
5
6
7

使用webpack打包是可以直接测试,输出node对象就是成功,可以找到对应的东西。

# 修改AST语法树及code转换

  • 把代码转换成语法树
  • 遍历所有节点,在语法树上找到对应节点
  • 对相应方法处理(处理之后还是语法树)
  1. 修改语法树要安装npm i @babel/traverse模块
  2. js中修改使用
// import traverse from "@babel/traverse";   export import 官网
const traverse = require('@babel/traverse').default  // require module.exports 可以解构

// 用的不一样的模块引入,webpack都可以使用

let code = `let name = 'zce is a ML'`

let ast = parser.parse(code)

// 上面的 ast 就是语法树,我要怎么遍历呢?利用插件 traverse 
traverse(ast, {
  // 这个方法会遍历到所有的节点
  enter(nodePath) {  
    // console.log(nodePath.node.type)
    // 每一个node下面都有一个type属性,Identifier是关键字
    if (nodePath.node.type === 'Identifier') {
      // 在本例中就说明我们找到了 name 变量名 ,并修改同级的name
      nodePath.node.name = 'aaa'
      // 因为是遍历,所以要把循环遍历停掉
      nodePath.stop()
    }
  }
})
let retCode = generator(ast)
console.log(retCode)
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
  1. 要把语法树转换成code需要安装npm i @babel/generator模块
  2. js写
import generator from '@babel/generator'
...
// 上面是转换好的语法树
const ret = generator(ast)
console.log(ret)
1
2
3
4
5

# 创建语法树及code转换

需求: 创建 let slogan = "我爱工作,我爱前端",节点添加至 body 中

  • 先想办法创建一个空的语法树(写一个变量为空再parse装换成语法树)
  • 依据目标语句,利用 babel 提供的插件或者方法来组装语法树的节点
  • 添加至 body
  1. 创建空的语法树,在js中写
let code = ``
let ast = parser.parse(code)  // 创建了一个空的语法树
console.log(ast)
1
2
3
  1. 需要安装npm i @babel/types,利用 types 里所提供的方法(type所对应的单词在 types中都有具体的方法,就相当于创建节点)
import * as t from '@babel/types'

...
// 利用方法自己创建一个语法树的节点:

// init,id这些都是语法树中的节点
// init是变量初始化
let init = t.stringLiteral('我爱工作,我爱前端')
// 变量名
let id = t.identifier('slogan')
// 变量声明,id和init放进去
let declarator = t.VariableDeclarator(id, init)
// 变量定义语句:添加kind关键字:let,
// 注意: 变量声明为数组,要包在数组里
let declaration = t.variableDeclaration('let', [declarator])

// push之前ast空语法树的body是个空数组,添加节点就push进去,组装完成
ast.program.body.push(declaration)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  1. 将语法树转为code
let retCode = generator(ast)
console.log(retCode)

1
2
3

# 删除语法树节点

1 travese 方法可以实现 AST 语法树的遍历操作

2 traverse 中不仅存在 enter 方法,只要是语法树中存在的节点类型,都有对应的方法可以直接调用

3 这些方法只有在遍历到对应类型的时候才会被调用

想要删除语法节点的核心就是先遍历找到所有的节点,这个操作可以通过 @babel/traverse 来完成, 找到每个节点之后就可以通过具体的方法来完成增删改查操作

  • NodePath 常用的属性
    • node: 获取当前节点
    • parent : 父节点
    • parentPath :父path
    • scope: 作用域
    • context : 上下文
  • NodePath 常用的方法
    • get: 当前节点
    • findParent:向父节点搜寻节点
    • getSibling: 获取兄弟节点
    • replaceWith: 用 AST 节点替换该节点
    • replaceWithMultiple :用多个 AST 节点替换该节点
    • insertBefore: 在节点前插入节点
    • insertAfter: 在节点后插入节点
    • remove: 删除节点
import * as parser from '@babel/parser'
import traverse from "@babel/traverse"
import generator from '@babel/generator'
import * as t from '@babel/types'

let code = `
  console.log('拉勾教育')
  let sum = 10 + 66 
  let minus = 88 - 66
  console.log("互联网人的实战大学")
`
let ast = parser.parse(code)
console.log(ast)

// traverse 遍历语法树的
traverse(ast, {
  Identifier(path) {
    if (path.node.name == 'sum') {
      path.parentPath.remove()
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# parse generator traverse types

更新时间: 2021-12-18 20:56