I am writing a swc plugin to transform tsx. And I want to replace 'props' to '$$props' in spreadElement. It seems trouble to solve. I have no way to replace it. The swc document can check in https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html How can I do? Here is my code, and the test case is in the bottom of the code.
use std::borrow::Cow;
use swc_core::{ecma::{ast::{Program, Expr, JSXElement}, visit::as_folder, parser::{Syntax, TsConfig}}, plugin::{proxies::TransformPluginProgramMetadata, plugin_transform}, common::comments::Comments};
use swc_core::ecma::{
transforms::testing::test,
visit::{FoldWith, VisitMut, VisitMutWith, Fold},
ast::*,
atoms::JsWord,
};
pub struct TransformVisitor;
impl VisitMut for TransformVisitor {
fn visit_mut_expr(&mut self, e: &mut Expr) {
e.visit_mut_children_with(self);
}
fn visit_mut_jsx_element(&mut self, e: &mut swc_core::ecma::ast::JSXElement) {
e.visit_mut_children_with(self);
println!("Ident: {:?}", e.opening.attrs);
if e.opening.attrs.len() > 0 {
let mut new_attrs = vec![];
for attr in e.opening.attrs.iter() {
let mut new_attr = attr.clone();
if let JSXAttrOrSpread::JSXAttr(attr) = &attr {
println!("attr new: {:?}", attr.name);
if let JSXAttrName::Ident(Ident { sym, .. }) = &attr.name {
if sym == "props" {
println!("props: {}", sym);
// new_attr.name = JSXAttrName::Ident(Ident::new("$$props".into(), attr.name.span));
}
}
} else if let JSXAttrOrSpread::SpreadElement(SpreadElement { expr, dot3_token, .. }) = &attr {
println!("attr new spread: {:?}", expr);
let mut new_expr = expr.clone();
// replace props to $$props
if let Expr::Object(ObjectLit { props, span, .. }) = &**expr {
println!("attr new spread props: {:?}", props);
let mut new_props = props.clone();
for j in 0..props.len() {
if let PropOrSpread::Prop(prop) = &props[j] {
if let Prop::KeyValue(KeyValueProp { key: PropName::Ident(Ident { sym, span, .. }), value }) = &**prop {
if sym == "props" {
println!("attr new spread props sym: {}", sym);
// please help me to replace props to $$props
new_props[j] = PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new("$$props".into(), span.clone())),
value: value.clone(),
})));
}
}
}
}
println!("attr new spread props new_props: {:?}", new_props);
new_expr = Box::new(Expr::Object(ObjectLit { props: new_props, span: span.clone() }));
}
new_attr = JSXAttrOrSpread::SpreadElement(SpreadElement { expr: new_expr, dot3_token: dot3_token.clone() });
}
new_attrs.push(new_attr);
}
e.opening.attrs = new_attrs;
}
}
}
/// An example plugin function with macro support.
/// `plugin_transform` macro interop pointers into deserialized structs, as well
/// as returning ptr back to host.
///
/// It is possible to opt out from macro by writing transform fn manually
/// if plugin need to handle low-level ptr directly via
/// `__transform_plugin_process_impl(
/// ast_ptr: *const u8, ast_ptr_len: i32,
/// unresolved_mark: u32, should_enable_comments_proxy: i32) ->
/// i32 /* 0 for success, fail otherwise.
/// Note this is only for internal pointer interop result,
/// not actual transform result */`
///
/// This requires manual handling of serialization / deserialization from ptrs.
/// Refer swc_plugin_macro to see how does it work internally.
#[plugin_transform]
pub fn my_plugin(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
program.fold_with(&mut as_folder(TransformVisitor))
}
// An example to test plugin transform.
// Recommended strategy to test plugin's transform is verify
// the Visitor's behavior, instead of trying to run `process_transform` with mocks
// unless explicitly required to do so.
test!(
Syntax::Typescript(TsConfig {
tsx: true,
..Default::default()
}),
|_| as_folder(TransformVisitor),
boo,
r#"<comp a="2" props={11} { ...{props: { a: 1 }} }></comp>"#,
r#"<comp a="2" props={11} { ...{props: { a: 1 }} }></comp>"#
);
replace 'props' to '$$props' in spreadElement