插入Number类型的节点
在第5篇中,已经记录了是如何利用babel系的库实现在源代码中,插入一个Number
类型的变量的。与之对应,我们可以继续利用acorn
来实现一个同样的需求,加深理解。
在此之前,先理一下插入节点的逻辑:
- 利用
node
的文件读写能力,读取需要操作的文件 - 利用
acorn.parse
将源代码字符串转换为AST
对象 - 利用
@babel/types
创建节点 - 往
ast.body
中顺序添加节点 - 利用
escodegen
将ast转化为源代码字符串 - 利用
prettier
将源代码字符串格式化
我们现在实现的是一个极其简单的新增节点逻辑,所以不存在"先查后增"的操作,只需要顺序添加进入ast对象就可以。
在前面已经介绍过是怎么利用types创建的节点,在我学习使用的过程中,我们会发现一个明显的特效,只要我们的对ast对象的改动是可以被正常解析的正常对象,最后生成的代码就不会有问题。
另外,还记得我们之前辛辛苦苦探索实现的,新增注释节点的例子吗?当时的难点主要是acorn
默认不解析BlockCommenet
和LineComment
,需要做一些特殊操作来实现这个新增注释需求。
我是被困了很久,最后才发现了解决这些问题的方法,不知道你们在写自己的测试用例的时候用了多久。
因为types
方法中的方法是直接修改的ast对象,所以有一个功能是可以利用这个特性,来一个快捷操作和链式操作,比如下面实现的这个例子:
javascript
import * as acorn from 'acorn'
import fs from 'fs-extra'
import escodegen from 'escodegen'
import { VariableDeclaration, VariableDeclarator, Identifier, addComment } from '@babel/types'
const filepath = './demo.js'
const workflow = async () => {
const source = await fs.readFile(filepath, { encoding: 'utf-8' })
// 利用acorn解析成ast
const ast = await acorn.parse(source, { sourceType: 'module', ecmaVersion: 2021 })
// 生成一个声明表达式节点
const var1 = VariableDeclaration('const', [
VariableDeclarator(Identifier('a_num1'), Identifier('12'))
])
// 这里调用@@babel/types中提供的addComment方法,添加一条块级注释
addComment(var1, 'leading', '这里是由acorn生成的注释', false)
// 直接在文件后追加节点
ast.body.push(var1)
// 利用escodegen提供的generate方法生成源代码
const output = await escodegen.generate(ast, {
comment: true
})
console.log('=========> output', output)
}
workflow()
我们可以将这个acorn
版本与babel
版本做一个对比,我们可以完全利用@babel/types
里面提供的方法去新增节点和添加注释节点。
所不同处,就是最后一步,将ast转换源代码。前面我也记录了为什么不可以利用@babel/core
的transformAstFromSync
方法去转换由acorn
转换的ast对象。转换的工具很多,并不局限于我所使用的两个库,但是转换的套路和逻辑大致上是一样的。
这就是我为什么要用两个不同的库来实现一个相同的需求。很多东西使用的时候互相对比就会发现很多有意思的东西,更容易接触到一些相当偏门的知识点。
源代码地址: