使用 TypeScript/JavaScript 编写 SWC 插件的可能性
SWC 是一个基于 Rust 实现的高性能 JavaScript/TypeScript 编译器,常常被用来替代 Babel 做代码转译、语法降级等工作。很多开发者熟悉 TypeScript 和 JavaScript 的编写逻辑,会好奇是否可以直接用这两种语言来编写 SWC 插件,避免学习 Rust 的成本。实际上,SWC 本身原生支持通过 Rust 编写插件,但借助社区生态和适配方案,我们确实可以用 TypeScript 或 JavaScript 实现部分场景下的 SWC 插件功能。
原生 SWC 插件的实现方式
SWC 原生的插件体系是基于 Rust 的,需要开发者使用 Rust 编写插件逻辑,编译为动态链接库后供 SWC 加载使用。这种方式性能最优,但门槛较高,适合对性能要求极高、且熟悉 Rust 开发的场景。以下是一个简单的 Rust 版 SWC 插件示例,功能是给所有函数调用添加日志前缀:
use swc_core::{
plugin::{
plugin_transform,
proxies::TransformPluginProgramMetadata,
},
ast::*,
visit::{VisitMut, VisitMutWith},
};
struct AddLogVisitor;
impl VisitMut for AddLogVisitor {
fn visit_mut_call_expr(&mut self, call: &mut CallExpr) {
// 遍历函数调用表达式,添加日志逻辑
// 这里可以对 call 进行修改,比如插入 console.log 语句
call.visit_mut_children_with(self);
}
}
#[plugin_transform]
pub fn process_program(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
let mut visitor = AddLogVisitor;
let mut program = program;
program.visit_mut_with(&mut visitor);
program
}用 TypeScript/JavaScript 编写 SWC 插件的方案
如果希望使用 TypeScript 或 JavaScript 编写 SWC 插件,目前主要有两种可行路径:一是使用 SWC 的 WASM 适配层,通过 JavaScript 调用 SWC 的编译能力并注入自定义逻辑;二是借助社区提供的 @swc/custom-transform 等工具,将 JavaScript 编写的转换逻辑集成到 SWC 的编译流程中。
方案一:基于 WASM 的 JavaScript 调用
SWC 提供了编译为 WASM 的版本,可以在 Node.js 或浏览器环境中通过 JavaScript 调用。我们可以自己编写 JavaScript 逻辑处理 AST,再结合 SWC 的 WASM 能力完成编译。以下是一个简单的示例,用 TypeScript 实现给所有变量声明添加注释的功能:
import { transformSync } from '@swc/wasm';
// 自定义转换逻辑,处理 AST 节点
function customTransform(ast: any) {
if (ast.type === 'VariableDeclaration') {
// 给变量声明节点添加自定义注释
ast.leadingComments = ast.leadingComments || [];
ast.leadingComments.push({
type: 'CommentLine',
value: ' 由自定义JS插件添加'
});
}
// 递归处理子节点
if (ast.children) {
ast.children.forEach((child: any) => customTransform(child));
}
return ast;
}
// 原始代码
const code = `
const a = 1;
const b = 2;
`;
// 先解析代码为 AST
const parseResult = transformSync(code, {
syntax: 'typescript',
isModule: false,
parse: true
});
// 应用自定义转换逻辑
const transformedAst = customTransform(parseResult.ast);
// 把转换后的 AST 重新编译为代码
const result = transformSync(transformedAst, {
syntax: 'typescript',
isModule: false,
code: true
});
console.log(result.code);方案二:使用社区适配工具
社区有一些工具封装了 SWC 的插件加载逻辑,允许开发者用 JavaScript 编写转换函数,然后注册到 SWC 的编译流程中。比如 swc-plugin-js 这类工具,可以让开发者导出一个 JavaScript 函数,接收 SWC 的 AST 节点作为参数,返回修改后的节点。以下是一个简单的示例:
// 插件入口文件 plugin.js
module.exports = function jsPlugin(node) {
// 处理函数调用表达式
if (node.type === 'CallExpression') {
const callee = node.callee;
// 如果调用的是 alert 函数,替换为 console.log
if (callee.type === 'Identifier' && callee.name === 'alert') {
callee.name = 'console.log';
}
}
return node;
};然后在 SWC 的配置文件 .swcrc 中注册这个插件:
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false
},
"transform": {
"plugins": [
["./plugin.js", {}]
]
}
}
}两种方案的优缺点对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 原生 Rust 插件 | 性能最优,和 SWC 核心能力完全对齐,支持所有 SWC 提供的 AST 操作能力 | 需要学习 Rust,开发门槛高,编译和调试流程相对复杂 |
| TypeScript/JavaScript 方案 | 开发门槛低,熟悉 JS/TS 的开发者可以快速上手,调试方便 | 性能略低于原生 Rust 插件,部分 SWC 底层的 AST 操作能力可能无法直接调用,依赖社区工具的更新维护 |
适用场景建议
如果你的项目对编译性能要求极高,或者需要实现非常复杂的 AST 转换逻辑,优先选择原生 Rust 编写 SWC 插件。如果是中小型项目,或者只是需要实现简单的代码转换逻辑(比如替换特定函数、添加简单注释、修改部分语法结构),使用 TypeScript 或 JavaScript 编写插件完全可以满足需求,还能大幅降低开发成本。
需要注意的是,目前 JavaScript 版的 SWC 插件生态还在不断完善中,部分高级特性可能支持不够完善,在选择方案前可以先测试自己需要的转换逻辑是否能够正常实现。另外,如果你使用的是构建工具(比如 Vite、Next.js)集成的 SWC,需要先确认构建工具是否支持自定义 JavaScript 插件的注册,避免兼容性问题。
SWC插件TypeScriptJavaScriptRustAST转换 本作品最后修改时间:2026-05-22 14:47:51