javascriptrustswc

Replace a TaggedTemplateExpression with a CallExpression in a SWC Rust Plugin


I'm working on a SWC Rust WASM plugin to transform JavaScript.

Overall SWC and its API are well designed. However currently I struggle to convert a TaggedTemplateExpression to a CallExpression like in this example:

console.log`Hello, ${name}!`;

to:

console.log("Hello, ", name, "!");

In Babel, this transformation would be relatively straightforward. Here's an example of how it might look:

const babel = require("@babel/core");

function transform({ types: t }) {
  return {
    visitor: {
      TaggedTemplateExpression(path) {
        const { tag, quasi } = path.node;

        if (
          t.isMemberExpression(tag) &&
          t.isIdentifier(tag.object, { name: "console" }) &&
          t.isIdentifier(tag.property, { name: "log" })
        ) {
          let args = [];

          quasi.quasis.forEach((element, index) => {
            args.push(t.stringLiteral(element.value.raw));
            if (index < quasi.expressions.length) {
              args.push(quasi.expressions[index]);
            }
          });

          path.replaceWith(
            t.callExpression(
              t.memberExpression(
                t.identifier("console"),
                t.identifier("log")
              ),
              args
            )
          );
        }
      },
    },
  };
}

I found the Changing AST Type section of the SWC CheatSheet but still I don't know how to convert it properly:

use swc_core::ecma::{
    ast::*,
    visit::{VisitMut, VisitMutWith},
};

struct TemplateToCallTransform;

impl VisitMut for TemplateToCallTransform {
    fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {=
        // How do I replace the current node with the new CallExpr?
    }
}

Solution

  • SWC author here (again).

    You need to handle it from fn visit_mut_expr(&mut self, e: &mut Expr).

    TaggedTpl and CallExpr both belongs to expression in ECMAScript, so you can handle it from there.

    
    use swc_core::ecma::{
        ast::*,
        visit::{VisitMut, VisitMutWith},
    };
    
    struct TemplateToCallTransform;
    
    impl VisitMut for TemplateToCallTransform {
        fn visit_mut_expr(&mut self, e: &mut Expr) {
            // You may skip this, depending on the requirements.
            e.visit_mut_children_with(self);
    
            match e {
                Expr::TaggedTpl(n) => {
                    *e = Expr::Call(CallExpr {
                        // ...fields
                    });
                }
    
                _ => {}
            }
        }
    }