插入Array类型的节点
在js中,数组类型是一个很特殊的类型,创建的方式并不是只有一种字面量的方法。除此之外,其他类型都可以使用构造函数创建。
思考一下,在js中创建数组有几种方法:
使用Array的构造函数,这里使用了
new
关键字,它很特殊,在babel
中它有一个单独的节点类型newExpression
。javascriptvar arr = new Array(1, 2, 3)
使用字面量,字面量就很简单了,在babel中可以直接使用
arrayExpression
表达式。javascriptvar arr = [1, 2, 3]
使用Array.from创建数组,下面两个都是由函数生成,需要用到
memberExpression
成员表达式和objectExpression
对象表达式。javascriptvar arr = Array.from({ 0: 1, 1: 2, length: 2 })
使用Array.of创建数组
javascriptvar a1 = Array.of({ name: 1 }, { name: 2 })
所以在babel中,也有对应的表达式,与上面创建的方法一一对应。
第一种使用构造函数中,使用了new
来实例化,对应了newExpression
new值表达式。
下面是对应的实现的代码:
new 关键字
javascript/** * @NOTE 使用new表达式构建数组 */ const newArr3 = t.newExpression(t.identifier('Array'), [ t.numericLiteral(1), t.numericLiteral(2) ]) const arr3 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('arr3'), newArr3) ])
可以使用由newExpression
new表达式声明一个Array
实例,其效果就是new Array(1, 2)
。到这里已经完成了等号右边的内容,还有左边的内容可以继续使用variableDeclaration
声明去创建。
字面量创建
javascript/** * @NOTE 这里使用的字面量表达式创建了一个变量arr2 */ const arr2 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('arr2'), t.arrayExpression([ t.numericLiteral(1), t.numericLiteral(2) ])) ])
在这里又回到了常规的套路,可以直接使用
arrayExpression
表达式,在内部添加数组元素就可以。我的这个例子是数组的类型就是Number
类型,其他类型都可以使用对应的函数创建。Array.from 函数
javascript/** * @NOTE 使用Array.from实现创建数组 * * @example Array.from({ 0: 1, 1: 2, length: 2 }) */ const arr4ObjValue = t.objectExpression([ t.objectProperty(t.numericLiteral(0), t.numericLiteral(1)), t.objectProperty(t.numericLiteral(1), t.numericLiteral(2)), t.objectProperty(t.identifier('length'), t.numericLiteral(2)), ]) const arr4 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('arr4'), t.callExpression( t.memberExpression(t.identifier('Array'), t.identifier('from')), [arr4ObjValue] )) ])
这个就很特殊了,它是由一个函数创建的,需要使用提供的
callExpresion
函数表达式,与此同时Array.from
期待一个对象作为入参,所以我们还需要准备一个对象节点,作为callExpress
的入参。可以使用
objectExpress
对象表达式创建一个函数节点,用objectProperty
给这个函数对象添加属性。用这种声明式方法去创建需要的节点是极其简单的,只需要提前梳理一下types
提供的方法就好啦。Array.of
javascript/** * @NOTE 使用Array.of 创建数组 * * @example Array.of(1, 2, 3) */ const arr5 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('arr5'), t.callExpression( t.memberExpression(t.identifier('Array'), t.identifier('of')), [t.numericLiteral(1), t.numericLiteral(2)] )) ])
与
Array.from
同理,Array.of
也是由函数驱动,所以我们同样需要callExpression
call表达式来创建右值部分。所不同的地方只有入参部分不一致,Array.of
期待由数组元素组成的入参。
如何在acorn中实现对应需求
在babel
内实现的需求代码直接搬到acorn
中执行时,会有一个明显的报错,如果你还有印象的话,就是我们之前说过的acorn
中都是直接量Liternal
没有numericLiternal
或者是其他的什么节点。
那么有没有什么一些方法可以做到?
最简单的方法,其实可以直接利用acorn.parse
将一段源代码解析成为AST
,然后把正常解析的AST
对象,重新放回目标AST
树。
第一步,先生成目标组织树
const workflow = async () => {
const source = await fs.readFile(filepath)
const ast = acorn.parse(source, { sourceType: 'module', ecmaVersion: 2021 })
}
第二步,将Array.from
与Array.of
这两种语句以源代码字符串,解析为正常AST
/**
* @NOTE 使用new关键字实例
*/
const arr3Node = t.newExpression(t.identifier('Array'), [t.identifier('1'), t.identifier('2')])
const arr3 = t.variableDeclaration('const', [
t.variableDeclarator(t.identifier('arr3'), arr3Node)
])
/**
* @NOTE 使用Array.from 构建数组
*/
const arr4Ast = acorn.parse(`const arr4 = Array.from({ 0: 1, length: 2 })`)
const arr4 = arr4Ast.body[0]
/**
* @NOTE 使用Array.of 构建数组
*/
const arr5Ast = acorn.parse(`const arr5 = Array.of(1, 2, 3)`)
const arr5 = arr5Ast.body[0]
ast.body.push(arr2, arr3, arr4, arr5)
在这里用这种取巧的方法已经不是我之前本意了,我们的目的就是为了通过使用babel
中提供的方法来认识AST
中的节点类型。
但是这里也给我们一点其法,所有问题并不是只有一种解法,为了达到目标去探索实现的方法,也是一种学习的过程。
总结:
- 数组类型是一个极其特殊的对象类型,它被允许以多种方式生成,所以会认识多个由
types
提供的方法,生成节点 - 在js中,每一种类型都有对应的构造函数,同理,可以创建其他数据类型的ast节点
源码地址: