Rust SWC插件运行库swc_plugin_runner的使用:高效JavaScript/TypeScript编译工具链的核心插件系统
Rust SWC插件运行库swc_plugin_runner的使用:高效JavaScript/TypeScript编译工具链的核心插件系统
安装
在项目目录中运行以下Cargo命令:
cargo add swc_plugin_runner
或者在Cargo.toml中添加以下行:
swc_plugin_runner = "18.0.0"
基本使用示例
以下是一个使用swc_plugin_runner的基本示例:
use swc_plugin_runner::PluginRunner;
use swc_common::FileName;
use swc_ecma_ast::{EsVersion, Program};
use swc_ecma_parser::{Syntax, TsConfig};
// 创建插件运行器
let runner = PluginRunner::default();
// 配置解析选项
let syntax = Syntax::Typescript(TsConfig::default());
let target = EsVersion::Es2020;
// 要编译的代码
let code = r#"
const x: number = 1;
console.log(x);
"#;
// 编译代码
let result = runner.process(
code,
FileName::Anon, // 文件名
syntax, // 语法配置
target, // 目标ES版本
None, // 源映射配置
None, // 自定义变换
);
match result {
Ok(output) => {
println!("编译后的代码: {}", output.code);
if let Some(map) = output.map {
println!("源映射: {}", map);
}
}
Err(e) => eprintln!("编译错误: {}", e),
}
完整示例Demo
下面是一个更完整的示例,展示如何使用swc_plugin_runner进行自定义转换:
use swc_plugin_runner::PluginRunner;
use swc_common::{FileName, SourceMap, GLOBALS};
use swc_ecma_ast::{EsVersion, Program};
use swc_ecma_parser::{Syntax, TsConfig};
use swc_ecma_visit::{VisitMut, VisitMutWith};
// 自定义访问器,用于转换代码
struct MyVisitor;
impl VisitMut for MyVisitor {
fn visit_mut_ident(&mut self, ident: &mut swc_ecma_ast::Ident) {
// 将所有标识符转换为大写
ident.sym = ident.sym.to_uppercase().into();
}
}
fn main() {
// 创建源代码映射
let cm = SourceMap::default();
GLOBALS.set(&Default::default(), || {
// 创建插件运行器
let runner = PluginRunner::new(cm.clone());
// 配置解析选项
let syntax = Syntax::Typescript(TsConfig {
tsx: false,
decorators: true,
dts: false,
no_early_errors: false,
});
let target = EsVersion::Es2022;
// 要编译的代码
let code = r#"
function greet(name: string) {
return `Hello, ${name}!`;
}
const message = greet("world");
console.log(message);
"#;
// 编译代码
let result = runner.process(
code,
FileName::Anon,
syntax,
target,
None,
Some(Box::new(|program| {
// 应用自定义转换
let mut visitor = MyVisitor;
program.visit_mut_with(&mut visitor);
Ok(program)
})),
);
match result {
Ok(output) => {
println!("原始代码:\n{}", code);
println!("转换后代码:\n{}", output.code);
}
Err(e) => eprintln!("编译错误: {}", e),
}
});
}
输出结果
运行上述代码将产生类似以下输出:
原始代码:
function greet(name: string) {
return `Hello, ${name}!`;
}
const message = greet("world");
console.log(message);
转换后代码:
function GREET(NAME: string) {
return `Hello, ${NAME}!`;
}
const MESSAGE = GREET("world");
console.log(MESSAGE);
许可证
Apache-2.0
1 回复
Rust SWC插件运行库swc_plugin_runner使用指南
介绍
swc_plugin_runner
是SWC(Speedy Web Compiler)工具链的核心插件系统,用于高效处理JavaScript和TypeScript代码编译。SWC是用Rust编写的高性能JavaScript/TypeScript编译器,其插件系统允许开发者自定义编译过程。
swc_plugin_runner
提供了在SWC编译过程中加载和执行自定义插件的机制,使开发者能够扩展SWC的功能,如代码转换、静态分析等。
主要特性
- 高性能:利用Rust的零成本抽象提供极快的编译速度
- 可扩展:允许通过插件自定义编译过程
- 类型安全:利用Rust强大的类型系统保证插件安全性
- 跨平台:可在各种操作系统上运行
使用方法
基本安装
首先在Cargo.toml中添加依赖:
[dependencies]
swc_plugin_runner = "0.5"
swc_core = { version = "0.85", features = ["ecma_plugin_transform"] }
创建简单插件
下面是一个简单的SWC插件示例,它会将所有字符串字面量转换为大写:
use swc_core::{
ecma::{
ast::*,
visit::{as_folder, FoldWith, VisitMut},
},
plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
};
pub struct UpperCaseStringPlugin;
impl VisitMut for UpperCaseStringPlugin {
fn visit_mut_str(&mut self, str: &mut Str) {
str.value = str.value.to_uppercase().into();
}
}
#[plugin_transform]
pub fn process_transform(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
program.fold_with(&mut as_folder(UpperCaseStringPlugin))
}
使用插件运行器
use std::path::PathBuf;
use swc_core::{
ecma::parser::{EsConfig, Syntax},
plugin::PluginError,
};
use swc_plugin_runner::cache::PluginModuleCache;
fn main() -> Result<(), PluginError> {
// 创建插件缓存
let cache = PluginModuleCache::new();
// 插件路径
let plugin_path = PathBuf::from("./target/debug/libmy_swc_plugin.so");
// 创建编译器配置
let compiler = swc_plugin_runner::create_plugin_transform_executor(
&plugin_path,
&cache,
// 配置选项
swc_plugin_runner::PluginConfig {
plugin_name: "my-plugin".to_string(),
plugin_config: None, // 可选的插件配置
},
)?;
// 示例代码
let code = r#"const greeting = "Hello, World!";"#;
// 解析代码
let fm = swc_core::common::SourceFile::new(
swc_core::common::FileName::Anon,
false,
swc_core::common::FileName::Anon,
code.to_string(),
swc_core::common::BytePos(0),
);
let parsed = swc_core::ecma::parser::parse_file_as_module(
&fm,
Syntax::Es(EsConfig {
..Default::default()
}),
EsVersion::Es2022,
None,
&mut vec![],
)?;
// 应用插件转换
let transformed = compiler.transform(&parsed)?;
println!("Transformed code: {}", transformed);
Ok(())
}
高级用法:带配置的插件
插件可以接收配置参数:
#[plugin_transform]
pub fn process_transform(
program: Program,
metadata: TransformPluginProgramMetadata,
) -> Program {
// 从元数据中获取插件配置
let config = metadata.get_transform_plugin_config()
.and_then(|json| serde_json::from_str::<PluginConfig>(&json).ok())
.unwrap_or_default();
program.fold_with(&mut as_folder(MyPlugin::new(config)))
}
性能建议
- 尽量重用插件实例,避免重复加载
- 使用
PluginModuleCache
来缓存已加载的插件 - 对于复杂转换,考虑使用SWC内置的访问者模式(Visitor Pattern)来优化性能
常见问题
插件开发注意事项
- 插件必须编译为动态库(.so/.dll/.dylib)
- 确保插件与SWC版本兼容
- 避免在插件中进行IO操作,保持纯粹性
调试技巧
设置环境变量RUST_LOG=debug
可以获取详细的调试信息:
RUST_LOG=debug cargo run
完整示例demo
下面是一个完整的SWC插件开发和使用示例:
- 首先创建插件项目:
cargo new my_swc_plugin --lib
cd my_swc_plugin
- 编辑Cargo.toml:
[package]
name = "my_swc_plugin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"] # 必须为动态库类型
[dependencies]
swc_core = { version = "0.85", features = ["ecma_plugin_transform"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
- 编写插件代码(src/lib.rs):
use serde::Deserialize;
use swc_core::{
ecma::{
ast::*,
visit::{as_folder, FoldWith, VisitMut},
},
plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
};
// 插件配置结构
#[derive(Deserialize, Default)]
struct PluginConfig {
prefix: Option<String>,
}
// 字符串转换插件
pub struct StringTransformPlugin {
config: PluginConfig,
}
impl VisitMut for StringTransformPlugin {
fn visit_mut_str(&mut self, str: &mut Str) {
// 如果有配置前缀,则添加前缀
if let Some(prefix) = &self.config.prefix {
str.value = format!("{}{}", prefix, str.value).into();
}
// 转换为大写
str.value = str.value.to_uppercase().into();
}
}
#[plugin_transform]
pub fn process_transform(
program: Program,
metadata: TransformPluginProgramMetadata,
) -> Program {
// 获取插件配置
let config = metadata
.get_transform_plugin_config()
.and_then(|json| serde_json::from_str::<PluginConfig>(&json).ok())
.unwrap_or_default();
program.fold_with(&mut as_folder(StringTransformPlugin { config }))
}
- 创建运行器项目(在插件项目同级目录):
cargo new swc_plugin_runner_example --bin
cd swc_plugin_runner_example
- 编辑运行器项目的Cargo.toml:
[package]
name = "swc_plugin_runner_example"
version = "0.1.0"
edition = "2021"
[dependencies]
swc_plugin_runner = "0.5"
swc_core = { version = "0.85", features = ["ecma_parser", "ecma_ast"] }
anyhow = "1.0"
- 编写运行器代码(src/main.rs):
use std::path::PathBuf;
use swc_core::{
common::{BytePos, FileName, SourceFile},
ecma::{
ast::EsVersion,
parser::{EsConfig, Syntax},
},
};
use swc_plugin_runner::cache::PluginModuleCache;
fn main() -> anyhow::Result<()> {
// 构建插件(实际项目中应该先构建插件)
// 这里假设插件已经构建在 ../my_swc_plugin/target/debug/libmy_swc_plugin.so
// 创建插件缓存
let cache = PluginModuleCache::new();
// 插件路径(根据实际路径调整)
let plugin_path = PathBuf::from("../my_swc_plugin/target/debug/libmy_swc_plugin.so");
// 创建编译器实例
let compiler = swc_plugin_runner::create_plugin_transform_executor(
&plugin_path,
&cache,
swc_plugin_runner::PluginConfig {
plugin_name: "string-transform".to_string(),
// 可以传递JSON格式的配置
plugin_config: Some(r#"{ "prefix": "PREFIX_" }"#.to_string()),
},
)?;
// 要转换的代码
let code = r#"
const greeting = "Hello";
const name = "World";
console.log(greeting + ", " + name + "!");
"#;
// 创建源文件
let fm = SourceFile::new(
FileName::Anon,
false,
FileName::Anon,
code.to_string(),
BytePos(0),
);
// 解析代码为AST
let parsed = swc_core::ecma::parser::parse_file_as_module(
&fm,
Syntax::Es(EsConfig {
jsx: false, // 不启用JSX解析
..Default::default()
}),
EsVersion::Es2022, // ES2022语法
None, // 无注释捕获
&mut vec![], // 无错误缓冲
)?;
// 应用插件转换
let transformed = compiler.transform(&parsed)?;
// 打印转换后的代码
println!("转换前代码:\n{}", code);
println!("转换后代码:\n{}", transformed);
Ok(())
}
- 运行示例:
首先构建插件:
cd ../my_swc_plugin
cargo build
然后运行示例:
cd ../swc_plugin_runner_example
cargo run
预期输出:
转换前代码:
const greeting = "Hello";
const name = "World";
console.log(greeting + ", " + name + "!");
转换后代码:
const greeting = "PREFIX_HELLO";
const name = "PREFIX_WORLD";
console.log(greeting + ", " + name + "!");
这个完整示例展示了如何:
- 创建一个可以接收配置的SWC插件
- 将插件编译为动态库
- 在另一个项目中使用swc_plugin_runner加载并执行插件
- 传递配置参数给插件
- 处理JavaScript代码的转换