跳至内容
文档
插件
ECMAScript
入门

实现插件

设置环境

安装所需的工具链

由于插件是用 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);foobar 调用。

让我们使用二元运算符缩小范围。

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 功能为您的插件移除日志记录。请参阅 相关文档 (在新标签页中打开)

发布您的插件

请参阅 插件发布指南