实现插件
设置环境
安装所需的工具链
由于插件是用 Rust 编程语言编写的,并作为 .wasm
文件构建的,因此您需要安装 Rust 工具链和 wasm 目标。
安装 Rust
您可以按照 Rust 官方网站上的“安装 Rust”页面 (在新标签页中打开) 中的说明进行操作。
将 wasm 目标添加到 Rust
SWC 支持两种类型的 .wasm
文件。它们是
- wasm32-wasi
- wasm32-unknown-unknown
在本指南中,我们将使用 wasm-wasi
作为目标。
安装 swc_cli
您可以通过以下操作安装基于 Rust 的 SWC CLI
cargo install swc_cli
配置 IDE
如果您要使用 vscode,建议安装 rust-analyzer
扩展。 rust-analyzer
是 Rust 编程语言的 语言服务器 (在新标签页中打开),它为代码补全、代码导航和代码分析提供了良好的功能。
实现简单插件
创建项目
SWC CLI 支持创建新的插件项目。
运行
swc plugin new --target-type wasm32-wasi my-first-plugin
# You should to run this
rustup target add wasm32-wasi
以创建新的插件,并使用您喜欢的 Rust IDE 打开 my-first-plugin
。
实现访问者
生成的代码具有
impl VisitMut for TransformVisitor {
// Implement necessary visit_mut_* methods for actual custom transform.
// A comprehensive list of possible visitor methods can be found here:
// https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html
}
它用于转换代码。 特质 VisitMut
(在新标签页中打开) 支持修改 AST 节点,并且由于它支持所有 AST 类型,因此它具有许多方法。
我们将使用
foo === bar;
作为输入。您可以从 SWC 游乐场 (在新标签页中打开) 获取此代码的实际表示形式。
{
"type": "Module",
"span": {
"start": 0,
"end": 12,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 0,
"end": 12,
"ctxt": 0
},
"expression": {
"type": "BinaryExpression",
"span": {
"start": 0,
"end": 11,
"ctxt": 0
},
"operator": "===",
"left": {
"type": "Identifier",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "foo",
"optional": false
},
"right": {
"type": "Identifier",
"span": {
"start": 8,
"end": 11,
"ctxt": 0
},
"value": "bar",
"optional": false
}
}
}
],
"interpreter": null
}
让我们为 BinExpr
实现一个方法。您可以像这样操作
use swc_core::{
ast::*,
visit::{VisitMut, VisitMutWith},
};
impl VisitMut for TransformVisitor {
fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) {
e.visit_mut_children_with(self);
}
}
请注意,如果您想为子项调用方法处理程序,则需要 visit_mut_children_with
。例如,visit_mut_ident
将由上面的 e.visit_mut_children_with(self);
为 foo
和 bar
调用。
让我们使用二元运算符缩小范围。
use swc_core::{
ast::*,
visit::{VisitMut, VisitMutWith},
common::Spanned,
};
impl VisitMut for TransformVisitor {
fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) {
e.visit_mut_children_with(self);
if e.op == op!("===") {
e.left = Box::new(Ident::new("kdy1".into(), e.left.span()).into());
}
}
}
op!("===")
是一个宏调用,它返回各种类型的运算符。在本例中,它返回 BinaryOp (在新标签页中打开),因为我们提供了 "==="
,它是一个二元运算符。有关更多详细信息,请参阅 op! 宏的 rustdoc (在新标签页中打开)。
如果我们运行此插件,我们将得到
kdy1 === bar;
测试您的转换
您可以简单地运行 cargo test
来测试您的插件。SWC 还提供了一个实用程序来简化夹具测试。
您可以轻松地验证转换的输入和输出。
test!(
Default::default(),
|_| as_folder(TransformVisitor),
boo,
r#"foo === bar;"#
);
然后,运行 UPDATE=1 cargo test
后,快照将被更新。
您可以查看 typescript 类型剥离器的实际夹具测试 (在新标签页中打开)。
#[testing::fixture("tests/fixture/**/input.ts")]
#[testing::fixture("tests/fixture/**/input.tsx")]
fn fixture(input: PathBuf) {
let output = input.with_file_name("output.js");
test_fixture(
Syntax::Typescript(TsConfig {
tsx: input.to_string_lossy().ends_with(".tsx"),
..Default::default()
}),
&|t| chain!(tr(), properties(t, true)),
&input,
&output,
);
}
需要注意的事项
- 提供给
testing::fixture
的 glob 相对于 cargo 项目目录。 - 输出文件是
output.js
,它存储在与输入文件相同的目录中。 test_fixture
驱动测试。- 您可以通过将语法传递给
test_fixture
来确定输入文件的语法。 - 然后,您将您的访问者实现作为第二个参数提供给
test_fixture
。 - 然后,您提供输入文件路径和输出文件路径。
日志记录
SWC 使用 tracing
进行日志记录。默认情况下,SWC 测试库将日志级别配置为 debug
,可以通过名为 RUST_LOG
的环境变量进行控制。例如,RUST_LOG=trace cargo test
将打印所有日志,包括 trace
日志。
如果需要,可以使用 tracing
的 Cargo 功能为您的插件移除日志记录。请参阅 相关文档 (在新标签页中打开)。
发布您的插件
请参阅 插件发布指南