swc-project/swc · error

failed to swcify arguments

Error message

failed to swcify arguments

What it means

`swc_estree_compat` converts Babel (ESTree) ASTs into swc ASTs via the `Swcify` trait. For `CallExpression`, each Babel `Arg` maps to `Option<ExprOrSpread>`; the impl (crates/swc_estree_compat/src/swcify/expr.rs:265) returns `None` for `Arg::Placeholder` and wraps every arg with `expect("failed to swcify arguments")`. So the panic fires when a call expression's argument list contains a Babel Placeholder node, which has no swc equivalent.

Source

Thrown at crates/swc_estree_compat/src/swcify/expr.rs:257

                Expression::Import(s) => Callee::Import(s.swcify(ctx)),
                _ => Callee::Expr(e.swcify(ctx)),
            },
        }
    }
}

impl Swcify for CallExpression {
    type Output = CallExpr;

    fn swcify(self, ctx: &Context) -> Self::Output {
        CallExpr {
            span: ctx.span(&self.base),
            callee: self.callee.swcify(ctx),
            args: self
                .arguments
                .swcify(ctx)
                .into_iter()
                .map(|v| v.expect("failed to swcify arguments"))
                .collect(),
            type_args: self.type_parameters.swcify(ctx).map(Box::new),
            ..Default::default()
        }
    }
}

impl Swcify for Arg {
    type Output = Option<ExprOrSpread>;

    fn swcify(self, ctx: &Context) -> Self::Output {
        Some(match self {
            Arg::Spread(s) => ExprOrSpread {
                spread: Some(ctx.span(&s.base)),
                expr: s.argument.swcify(ctx),
            },
            Arg::JSXName(e) => ExprOrSpread {
                spread: None,

View on GitHub (pinned to 5176682b65)

Solutions

  1. Do not enable the Babel parser's placeholder option (or strip `%%...%%` placeholders) before converting the AST.
  2. Pre-process the Babel AST: replace Placeholder args with real expressions or remove them, then call swcify.
  3. If you truly need templated call sites, keep the tree on the Babel side; swc's AST has no placeholder node for arguments.

Example fix

// before
let parser = Parser::new(...).with_placeholder_plugin(); // enables %%x%% args
let swc_ast = babel_call_expr.swcify(&ctx); // Arg::Placeholder -> panic

// after
// parse without the placeholder plugin, or replace placeholder args first
let swc_ast = babel_call_expr.swcify(&ctx);
Defensive patterns

Strategy: type-guard

Type guard

// Reject Babel CallExpressions containing Placeholder args before swcify.
fn call_args_convertible(call: &swc_estree_compat::babel_ast::CallExpression) -> bool {
    call.arguments.iter().all(|arg| {
        !matches!(
            arg,
            swc_estree_compat::babel_ast::Arg::Placeholder(_)
        )
    })
}

Prevention

When it happens

Trigger: Calling `ast.swcify(&ctx)` on a Babel AST parsed with the `placeholders` feature (syntax like `foo(%%x%%)`), so call arguments carry `Arg::Placeholder` nodes. Any other argument kind (Expr, Spread, JSXName) converts fine.

Common situations: Consuming Babel fixture ASTs or parser output that enables placeholder syntax (used by Babel's own tests for template holes); deserializing Babel JSON containing `"Placeholder"` argument nodes and converting it to swc.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/9c533b0ed5d6549e. Report an issue: GitHub.