Rust宏插件库swc_plugin_macro的使用:SWC编译器自定义转换与代码生成宏工具

Rust宏插件库swc_plugin_macro的使用:SWC编译器自定义转换与代码生成宏工具

安装

在项目目录中运行以下Cargo命令:

cargo add swc_plugin_macro

或者在Cargo.toml中添加以下行:

swc_plugin_macro = "1.1.0"

使用示例

swc_plugin_macro是一个用于创建SWC编译器插件的宏库,允许开发者自定义代码转换逻辑。下面是一个完整的示例:

use swc_plugin_macro::plugin_module;
use swc_core::{
    ecma::{
        ast::*,
        visit::{VisitMut, VisitMutWith},
    },
    plugin::{
        metadata::TransformPluginMetadataContextKind,
        plugin_transform,
    },
};

// 定义一个简单的转换插件,将变量名"foo"重命名为"bar"
#[plugin_module]
pub fn transform(program: Program, _metadata: TransformPluginMetadataContextKind) -> Result<Program, String> {
    let mut visitor = RenameVisitor;
    program.visit_mut_with(&mut visitor);
    Ok(program)
}

// 自定义访问器实现变量重命名
struct RenameVisitor;

impl VisitMut for RenameVisitor {
    fn visit_mut_ident(&mut self, ident: &mut Ident) {
        if ident.sym == "foo" {
            ident.sym = "bar".into();
        }
    }
}

// 导出插件入口点
#[plugin_transform]
pub fn process_transform(program: Program, metadata: TransformPluginMetadataContextKind) -> Program {
    transform(program, metadata).unwrap()
}

完整示例代码

以下是一个更完整的示例,展示了如何使用swc_plugin_macro创建自定义转换插件:

use swc_plugin_macro::{plugin_module, plugin_transform};
use swc_core::{
    ecma::{
        ast::*,
        visit::{VisitMut, VisitMutWith},
    },
    plugin::metadata::TransformPluginMetadataContextKind,
};

// 主转换函数,使用plugin_module宏标记
#[plugin_module]
pub fn transform(
    mut program: Program, 
    _metadata: TransformPluginMetadataContextKind
) -> Result<Program, String> {
    // 创建自定义访问器实例
    let mut visitor = CustomVisitor::new();
    // 遍历并修改AST
    program.visit_mut_with(&mut visitor);
    Ok(program)
}

// 自定义访问器结构体
struct CustomVisitor;

impl CustomVisitor {
    fn new() -> Self {
        CustomVisitor
    }
}

// 实现VisitMut trait来修改AST
impl VisitMut for CustomVisitor {
    // 处理标识符
    fn visit_mut_ident(&mut self, ident: &mut Ident) {
        if ident.sym == "old_name" {
            ident.sym = "new_name".into();
        }
    }
    
    // 处理函数声明
    fn visit_mut_function(&mut self, func: &mut Function) {
        // 先处理子节点
        func.visit_mut_children_with(self);
        
        // 修改函数名
        if let Some(ident) = &mut func.ident {
            if ident.sym == "old_function" {
                ident.sym = "new_function".into();
            }
        }
    }
}

// 插件入口点,使用plugin_transform宏标记
#[plugin_transform]
pub fn process_transform(
    program: Program, 
    metadata: TransformPluginMetadataContextKind
) -> Program {
    transform(program, metadata).unwrap()
}

主要功能

  1. #[plugin_module]宏:简化SWC插件模块的创建
  2. #[plugin_transform]宏:提供插件转换的入口点
  3. 与SWC编译器生态无缝集成
  4. 支持自定义AST转换逻辑

许可证

Apache-2.0


1 回复

Rust宏插件库swc_plugin_macro的使用:SWC编译器自定义转换与代码生成宏工具

介绍

swc_plugin_macro是一个用于为SWC编译器创建自定义转换插件的Rust宏库。SWC是一个用Rust编写的超快速TypeScript/JavaScript编译器,而swc_plugin_macro允许开发者通过Rust宏来扩展SWC的功能,实现自定义的代码转换和生成。

这个库特别适合需要:

  • 自定义JavaScript/TypeScript代码转换
  • 实现特定领域的代码优化
  • 开发自定义的代码生成工具
  • 创建编译时的元编程功能

使用方法

基本安装

首先,在Cargo.toml中添加依赖:

[dependencies]
swc_plugin_macro = "0.2"
swc_core = { version = "0.82", features = ["ecma_plugin_transform"] }

创建基本插件

use swc_plugin_macro::plugin_transform;
use swc_core::ecma::ast::*;
use swc_core::ecma::visit::VisitMut;

#[plugin_transform]
pub fn process_transform(program: Program, _plugin_config: String) -> Program {
    // 这里实现你的转换逻辑
    program
}

示例:转换所有标识符为大写

use swc_plugin_macro::plugin_transform;
use swc_core::ecma::ast::*;
use swc_core::ecma::visit::{VisitMut, as_folder};

struct UpperCaseVisitor;

impl VisitMut for UpperCaseVisitor {
    fn visit_mut_ident(&mut self, ident: &mut Ident) {
        ident.sym = ident.sym.to_uppercase().into();
    }
}

#[plugin_transform]
pub fn uppercase_transform(program: Program, _config: String) -> Program {
    program.fold_with(&mut as_folder(UpperCaseVisitor))
}

示例:自定义JSX转换

use swc_plugin_macro::plugin_transform;
use swc_core::ecma::ast::*;
use swc_core::ecma::visit::{VisitMut, as_folder};

struct JsxTransformVisitor;

impl VisitMut for JsxTransformVisitor {
    fn visit_mut_jsx_element(&mut self, el: &mut JSXElement) {
        // 转换所有JSX元素
        if let JSXElementName::Ident(ident) = &el.opening.name {
            if ident.sym == "div" {
                el.opening.name = JSXElementName::Ident(Ident::new("section".into(), el.opening.name.span()));
            }
        }
    }
}

#[plugin_transform]
pub fn jsx_transform(program: Program, _config: String) -> Program {
    program.fold_with(&mut as_folder(JsxTransformVisitor))
}

配置插件

plugin_transform宏允许通过第二个参数传递配置字符串:

#[plugin_transform]
pub fn configured_transform(program: Program, config: String) -> Program {
    println!("Received config: {}", config);
    // 解析配置并应用到转换逻辑
    program
}

构建和使用

  1. 将你的插件构建为动态库:
[lib]
crate_type = ["cdylib"]
  1. 在SWC配置中引用:
{
  "jsc": {
    "experimental": {
      "plugins": [
        ["path/to/your/plugin.wasm", {}]
      ]
    }
  }
}

高级用法

访问编译器上下文

use swc_plugin_macro::plugin_transform;
use swc_core::{
    ecma::ast::*,
    plugin::{metadata::TransformPluginMetadataContext, plugin_transform},
};

#[plugin_transform]
pub fn context_aware_transform(
    program: Program,
    metadata: TransformPluginMetadataContext,
) -> Program {
    let filename = metadata
        .get_context(&program)
        .and_then(|ctx| ctx.filename)
        .unwrap_or_default();
    
    println!("Processing file: {}", filename);
    program
}

代码生成示例

use swc_plugin_macro::plugin_transform;
use swc_core::ecma::ast::*;

#[plugin_transform]
pub fn codegen_transform(program: Program, _config: String) -> Program {
    if let Program::Module(mut module) = program {
        // 在模块末尾添加console.log语句
        module.body.push(ModuleItem::Stmt(Stmt::Expr(ExprStmt {
            span: Default::default(),
            expr: Box::new(Expr::Call(CallExpr {
                span极客时间 年卡
                span: Default::default(),
                callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                    span: Default::default(),
                    obj: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident::new(
                        "console".into(),
                        Default::default(),
                    ))),
                    prop: Box::new(Expr::Ident(Ident::new(
                        "log".into(),
                        Default::default(),
                    ))),
                    computed: false,
                })),
                args: vec![Lit::Str(Str {
                    span: Default::default(),
                    value: "Transformed by swc plugin!".into(),
                    raw: None,
                })
                .as_arg()],
                type_args: None,
            })),
        })));
        
        Program::Module(module)
    } else {
        program
    }
}

完整示例

下面是一个完整的SWC插件示例,实现了将代码中的特定函数调用替换为其他函数:

// Cargo.toml
/*
[dependencies]
swc_plugin_macro = "0.2"
swc_core = { version = "0.82", features = ["ecma_plugin_transform"] }
*/

use swc_plugin_macro::plugin_transform;
use swc_core::ecma::ast::*;
use swc_core::ecma::visit::{VisitMut, as_folder};

// 定义访问器结构体
struct FunctionReplaceVisitor {
    // 可以在这里添加配置参数
}

impl VisitMut for FunctionReplaceVisitor {
    fn visit_mut_call_expr(&mut self, call: &mut CallExpr) {
        // 检查是否是我们要替换的函数调用
        if let Callee::Expr(expr) = &call.callee {
            if let Expr::Ident(ident) = &**expr {
                if ident.sym == "oldFunction" {
                    // 替换为新的函数调用
                    call.callee = Callee::Expr(Box::new(Expr::Ident(Ident::new(
                        "newFunction".into(),
                        ident.span,
                    ))));
                }
            }
        }
    }
}

// 使用宏标记插件入口点
#[plugin_transform]
pub fn function_replace_plugin(program: Program, _config: String) -> Program {
    // 应用我们的访问器
    program.fold_with(&mut as_folder(FunctionReplaceVisitor {}))
}

注意事项

  1. 插件必须编译为WASM格式才能在SWC中使用
  2. 性能敏感的转换应该尽量减少AST遍历次数
  3. 复杂的转换可能需要多次遍历AST
  4. 注意保持源映射信息以确保调试体验

swc_plugin_macro为Rust开发者提供了强大的能力来扩展SWC编译器的功能,实现各种自定义的代码转换和生成需求。

回到顶部