Rust开发SWC插件:如何创建ExprOrSpread类型的Vec替换参数?
解决SWC插件中替换console.log参数的问题
先搞懂ExprOrSpread是什么
ExprOrSpread是SWC AST定义的枚举类型,用来表示单个函数参数——要么是普通表达式(比如字符串字面量、变量),要么是带...展开运算符的表达式(比如...arr)。所以CallExpr.args必须是这个类型的数组,不能直接赋值字符串。
实现步骤
要把console.log("hello")改成console.log("world"),需要构造对应的AST节点并替换参数:
- 构造字符串字面量的Expr节点:用
Expr::Lit包裹Lit::Str,定义字符串内容和位置信息(Span)。 - 包装成ExprOrSpread:因为不需要展开运算符,所以
spread字段设为None。 - 替换CallExpr的args数组:把新构造的
ExprOrSpread放入数组,替换原参数。
完整代码示例
use swc_core::ecma::ast::{CallExpr, Expr, ExprOrSpread, Lit, Str}; use swc_core::ecma::visit::{VisitMut, VisitMutWith}; use swc_core::common::Span; // 定义自定义访问器,处理Call表达式 struct ConsoleLogTransformer; impl VisitMut for ConsoleLogTransformer { fn visit_mut_call_expr(&mut self, n: &mut CallExpr) { // 校验是否是console.log调用 if let Expr::Member(member_expr) = &*n.callee { if let Expr::Ident(obj) = &*member_expr.obj { if obj.sym == "console" { if let Expr::Ident(prop) = &*member_expr.prop { if prop.sym == "log" { // 构造"world"字符串的AST节点 let world_str = Str { value: "world".into(), span: Span::dummy(), // 若需保留原位置,可替换为n.args.first().map(|a| a.expr.span).unwrap_or(Span::dummy()) has_escape: false, }; let lit_expr = Expr::Lit(Lit::Str(world_str)); // 将表达式包装为ExprOrSpread let new_arg = ExprOrSpread { expr: Box::new(lit_expr), spread: None, }; // 替换参数数组 n.args = vec![new_arg]; } } } } } } } // 插件入口的transform函数 pub fn transform(program: Program, _ctx: TransformPluginContext) -> Program { program.visit_mut_with(&mut ConsoleLogTransformer) }
关键细节说明
- Span处理:
Span::dummy()是占位的位置信息,如果希望保留原代码的位置关联(比如错误提示),可以复用原参数的span,比如n.args.first().map(|a| a.expr.span).unwrap_or(Span::dummy())。 - console.log的判断逻辑:通过逐层拆解Member表达式,确认调用的是
console对象的log方法,避免误改其他函数调用。 - ExprOrSpread的必要性:SWC用这个类型统一处理普通参数和展开参数,所以即使是普通字符串,也必须包裹成这个类型才能放入
args数组。
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

