实现插件
¥Implementing a plugin
重要的 API 更改
¥Important API Changes
可能会影响插件开发的最近 API 更改:
¥Recent API changes that may affect your plugin development:
-
用元组替换
chain!宏:使用(代替chain!(。你可以使用 IDE 功能将所有chain!(替换为(。¥Replace
chain!macro with tuples: Use(instead ofchain!(. You can replace allchain!(with(using IDE features. -
对于具有 13 个以上参数的
chain!:对第 13 个元素之后的项目使用嵌套元组。¥For
chain!with 13+ arguments: Use nested tuples for items after the 13th element. -
一般用
-> impl Pass替换-> impl Fold。¥Replace
-> impl Foldwith-> impl Passin general. -
as_folder现在是visit_mut_pass,返回impl VisitMut + Pass而不是impl VisitMut + Fold。¥
as_folderis nowvisit_mut_passand returnsimpl VisitMut + Passinstead ofimpl VisitMut + Fold. -
使用
Program.apply和Program.mutate将 Pass 应用于程序:¥Use
Program.applyandProgram.mutateto apply Pass to program:-
fn apply(self, impl Pass) -> Self -
fn mutate(&mut self, impl Pass)
-
-
用
noop_pass()替换noop()。新功能位于swc_ecma_ast中,是一个真正的 noop 功能。¥Replace
noop()withnoop_pass(). The new function lives inswc_ecma_astand is a real noop function.
设置环境
¥Setup environment
安装所需的工具链
¥Install required toolchain
由于插件是用 Rust 编程语言编写并构建为 .wasm 文件,因此你需要安装 rust 工具链和 wasm 目标。
¥As plugin is written in the rust programming language and built as a .wasm file, you need to install rust toolchain and wasm target.
安装 Rust
¥Install rust
你可以按照 Rust 官方网站的 ‘安装 Rust’ 页面 的说明进行操作
¥You can follow instructions at ‘Install Rust’ page from the official rust website
将 wasm 目标添加到 Rust
¥Add wasm target to rust
SWC 支持两种 .wasm 文件。那些是
¥SWC supports two kinds of .wasm files.
Those are
-
wasm32-wasip1
-
wasm32-未知-未知
¥wasm32-unknown-unknown
在本指南中,我们将使用 wasm-wasip1 作为目标。
¥In this guide, we will use wasm-wasip1 as a target.
安装 swc_cli
¥Install swc_cli
你可以通过执行以下操作为 SWC 安装基于 Rust 的 CLI
¥You can install a rust-based CLI for SWC by doing
cargo install swc_cli配置 IDE
¥Configuring IDE
如果你要使用 vscode,建议安装 rust-analyzer 扩展。rust-analyzer 是 Rust 编程语言的 语言服务器 ,它为代码完成、代码导航和代码分析提供了良好的功能。
¥If you are going to use vscode, it’s recommended to install rust-analyzer extension.
rust-analyzer is a language server for the rust programming language, which provides good features for code completion, code navigation, and code analysis.
实现简单的插件
¥Implementing simple plugin
创建一个项目
¥Create a project
SWC CLI 支持创建新的插件项目。
¥SWC CLI supports creating a new plugin project.
运行
¥Run
swc plugin new --target-type wasm32-wasip1 my-first-plugin
# You should to run this
rustup target add wasm32-wasip1创建一个新插件,然后使用你首选的 Rust IDE 打开 my-first-plugin。
¥to create a new plugin, and open my-first-plugin with your preferred rust IDE.
实现访客
¥Implementing a visitor
生成的代码有
¥The generated code has
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 类型,因此它有很多方法。
¥which is used to transform code.
The trait VisitMut supports mutating AST nodes, and as it supports all AST types, it has lots of methods.
我们将使用
¥We will use
foo === bar;作为输入。从 SWC 在线运行 开始,你可以得到这段代码的实际表示。
¥as the input. From the SWC Playground , you can get actual representation of this code.
{
"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 实现一个方法。你可以这样做
¥Let’s implement a method for BinExpr.
You can do it like
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。例如 foo 和 bar 的 visit_mut_ident 将被上面的 e.visit_mut_children_with(self); 调用。
¥Note that visit_mut_children_with is required if you want to call the method handler for children.
e.g. visit_mut_ident for foo and bar will be called by e.visit_mut_children_with(self); above.
让我们使用二元运算符缩小范围。
¥Let’s narrow down it using the binary operator.
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_no_ctxt("kdy1".into(), e.left.span()).into());
}
}
}op!("===") 是一个宏调用,它返回各种类型的运算符。在本例中它返回 BinaryOp ,因为我们提供了 "===",它是一个二元运算符。详细信息请参见 op 的 rustdoc!macro 。
¥op!("===") is a macro call, and it returns various types of operators.
It returns BinaryOp in this case, because we provided "===", which is a binary operator.
See the rustdoc for op! macro for more details.
如果我们运行这个插件,我们会得到
¥If we run this plugin, we will get
kdy1 === bar;测试你的变换
¥Testing your transform
你只需运行 cargo test 即可测试你的插件。SWC 还提供了一个实用程序来简化夹具测试。
¥You can simply run cargo test to test your plugins.
SWC also provides a utility to ease fixture testing.
你可以轻松验证转换的输入和输出。
¥You can easily verify the input and output of the transform.
test!(
Default::default(),
|_| visit_mut_pass(TransformVisitor), // Note: Updated to use visit_mut_pass instead of as_folder
boo,
r#"foo === bar;"#
);然后,一旦运行 UPDATE=1 cargo test,快照就会更新。
¥Then, once you run UPDATE=1 cargo test, the snapshot will be updated.
你可以看看 Typescript Type Stripper 的真实夹具测试 。
¥You can take a look at the real fixture test for typescript type stripper .
#[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| (tr(), properties(t, true)), // Note: Updated to use tuple syntax instead of chain!
&input,
&output,
);
}注意事项:
¥Things to note:
-
提供给
testing::fixture的 glob 是相对于 cargo 项目目录的。¥The glob provided to
testing::fixtureis relative to the cargo project directory. -
输出文件是
output.js,它与输入文件存储在同一目录中。¥The output file is
output.js, and it’s stored in a same directory as the input file. -
test_fixture驱动测试。¥
test_fixturedrives the test. -
你可以通过将语法传递给
test_fixture来确定输入文件的语法。¥You can determine the syntax of the input file by passing the syntax to
test_fixture. -
然后,你提供访问者实现作为
test_fixture的第二个参数。¥You then provide your visitor implementation as the second argument to
test_fixture. -
然后提供输入文件路径和输出文件路径。
¥Then you provide the input file path and the output file path.
日志
¥Logging
SWC 使用 tracing 进行日志记录。默认情况下,SWC 测试库将日志级别默认配置为 debug,这可以通过使用名为 RUST_LOG 的环境变量来控制。例如 RUST_LOG=trace cargo test 将打印所有日志,包括 trace 日志。
¥SWC uses tracing for logging.
By default, SWC testing library configures the log level to debug by default, and this can be controlled by using an environment variable named RUST_LOG.
e.g. RUST_LOG=trace cargo test will print all logs, including trace logs.
如果需要,你可以使用 tracing 的 Cargo 功能删除插件的日志记录。参见 它的文档 。
¥If you want, you can remove logging for your plugin by using cargo features of tracing.
See the documentation for it .
发布你的插件
¥Publishing your plugin
请参阅 插件发布指南
¥Please see plugin publishing guide