Babel插件开发小试:支持模块化、代码复用化
一、解决上期问题
上期提到,如果用如下方式导出插件会报错:
1 | export default function ({ types: t }) { |
解决方式是在package.json根对象下加入
1 | "type": "module" |
这样,所有.js文件都会当作ES module处理。如果想按CommonJS处理,将文件扩展名改为.cjs。同理,如果type是commonjs,所有ES module文件的扩展名应为.mjs。
注意,用ESM引入CJS模块时:
1 | // lib.js |
如果这样引用模块就会报错traverse不是函数。因为这里引入的是整个exports对象,所以需要如下写:
1 | import traverse from "./lib.js" |
这点我们后面会用到。
二、支持导入重载
1 | import { $operator as $ } from "./operator.js" |
现在会被编译为如下代码(如果$operator重载了加法):
1 | import { $operator as $ } from "./operator.js" |
编译后重载对象的名称都是as后面的名称,import必须为具名导入,as不是必需的,不能为:
1 | import $operator from "./operator.js" |
这与插件的工作原理有关。
三、导入的工作原理
1 | Program(path, state) { |
插件会先遍历import语句,并判断import进来的名字是不是babel配置文件中配置的重载对象名operatorObjectName。然后将$operator所在的文件解析为绝对路径,用于后续读取。specifier.local.name在有as的情况下为as后的值,没有则是原来的值,即specifier.imported.name。
1 | import parser from "@babel/parser"; |
接下来读取$operator所在的文件为字符串,并讲其解析为抽象语法树,然后遍历其抽象语法树,找到重载对象的声明并注册重载。
我们需要在Babel配置文件中配置被读取的文件的编码,默认值为utf8。
1 | "plugins": [["./plugin-operator/main.js", { "encoding": "utf8" }]] |
此处我们用到了两个Babel API。parser.parse读取字符串并将其解析为AST,只有在第二个参数中传入sourceType: "module"才能解析ESM,否则会报错。
四、复用代码
注意到,本插件中所有的visitor都哦有类似的如下结构:
1 | const operatorObjectParent = path.findParent((parentPath) => t.isVariableDeclaration(parentPath) && operatorObjName == parentPath.node.declarations?.[0].id.name); |
我们可以将这些代码提取出来以优化代码结构。每个visitor都是一个函数,因此我们可以创建一个高阶函数,他接受一个函数replacement,这是所有visitor唯一不同的地方,并返回一个拥有相应replacement的函数。
1 | const visitorFactory = (replacement) => (path) => { |
但只有这样是不够的,因为replacement需要访问path和operator,所以我们需要将replacement改成一个函数,将path和operator传递给他。
1 | const visitorFactory = (replacement) => (path) => { |
于是visitor可以改造成这样:
1 | AssignmentExpression: visitorFactory((operator, path) => t.parenthesizedExpression( |
对于自增减,我们进行如下的特殊处理:
1 | if (path.node.prefix) { |
我们直接在这里操作抽象语法树节点,并返回节点自身,这样在visitorFactory中就不会再修改了。
- 标题: Babel插件开发小试:支持模块化、代码复用化
- 作者: Stone926
- 创建于 : 2024-11-17 18:38:15
- 更新于 : 2025-11-04 20:34:39
- 链接: https://stone926.github.io/2024/11/17/babel-plugin-operator2/
- 版权声明: 本文为公有领域作品,可自由转载、引用。