プラグインの実装
環境設定
必要なツールチェーンのインストール
プラグインは Rust プログラミング言語で記述され、.wasm
ファイルとしてビルドされるため、Rust ツールチェーンと wasm ターゲットをインストールする必要があります。
Rust のインストール
公式 Rust ウェブサイトの「Rust のインストール」ページ (新しいタブで開きます)の手順に従ってください。
Rust に wasm ターゲットを追加
SWC は 2 種類の .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 Playground (新しいタブで開きます)から、このコードの実際表現を取得できます。
{
"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
が必要であることに注意してください。たとえば、foo
および bar
の visit_mut_ident
は、上記の e.visit_mut_children_with(self);
によって呼び出されます。
バイナリ演算子を使用して範囲を絞り込みましょう。
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
の 2 番目の引数として、ビジターの実装を提供します。 - 次に、入力ファイルパスと出力ファイルパスを提供します。
ロギング
SWCはロギングにtracing
を使用しています。デフォルトでは、SWCのテストライブラリはログレベルをデフォルトでdebug
に設定しており、これはRUST_LOG
という名前の環境変数を使用して制御できます。例えば、RUST_LOG=trace cargo test
とすると、trace
ログを含むすべてのログが出力されます。
必要であれば、tracing
のcargo機能を使用することで、プラグインのロギングを削除できます。ドキュメント(新しいタブで開きます)を参照してください。
プラグインの公開
プラグイン公開ガイドをご覧ください。