swc-project/swc · error

illegal conversion: Cannot convert {:?} to ExprOutput - babe

Error message

illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent

What it means

Expr::JSXMember — the `Foo.Bar` in an element name like `<Foo.Bar />` — has no standalone Babel Expression equivalent (Babel's JSXMemberExpression is only legal inside JSX names), so the generic expression conversion panics at expr.rs:146. Normally JSXMember lives inside JSXElementName and is handled by the JSX element babelify path; hitting this arm means a JSXMember node was lifted into a plain expression slot.

Source

Thrown at crates/swc_estree_compat/src/babelify/expr.rs:146

            }
            Expr::JSXElement(e) => {
                ExprOutput::Expr(Box::alloc().init(Expression::JSXElement(e.babelify(ctx))))
            }
            Expr::JSXFragment(f) => {
                ExprOutput::Expr(Box::alloc().init(Expression::JSXFragment(f.babelify(ctx))))
            }
            Expr::TsTypeAssertion(a) => {
                ExprOutput::Expr(Box::alloc().init(Expression::TSTypeAssertion(a.babelify(ctx))))
            }
            Expr::TsNonNull(n) => {
                ExprOutput::Expr(Box::alloc().init(Expression::TSNonNull(n.babelify(ctx))))
            }
            Expr::TsAs(a) => ExprOutput::Expr(Box::alloc().init(Expression::TSAs(a.babelify(ctx)))),
            Expr::TsInstantiation(..) => unimplemented!("Babel doesn't support this right now."),
            Expr::PrivateName(p) => ExprOutput::Private(p.babelify(ctx)),

            // TODO(dwoznicki): how does babel handle these?
            Expr::JSXMember(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::JSXNamespacedName(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::JSXEmpty(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::TsConstAssertion(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::TsSatisfies(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self

View on GitHub (pinned to 5176682b65)

Solutions

  1. Keep member element names inside JSXElement; let the JSX babelify path convert them.
  2. Lower JSX first with the react transform so babelify never sees JSXMember.
  3. If a name must be an expression, rewrite it to a plain Expr::Member (Ident.member) before babelify.

Example fix

// before: lifted into an expression slot
Expr::JSXMember(jsx_member_foo_bar)

// after: plain member expression
member_expr(ident!("Foo"), ident_name!("Bar"))
Defensive patterns

Strategy: type-guard

Type guard

fn has_jsxmember_in_expr(program: &Program) -> bool {
    struct Scan(bool);
    impl Visit for Scan {
        fn visit_expr(&mut self, e: &Expr) {
            if matches!(e, Expr::JSXMember(_)) { self.0 = true; }
            e.visit_children_with(self);
        }
        fn visit_jsx_element(&mut self, _: &JSXElement) {}
    }
    let mut s = Scan(false);
    program.visit_with(&mut s);
    s.0
}

Prevention

When it happens

Trigger: Babelifying an AST where a JSXMember appears as a general Expr — e.g. a plugin/codemod extracted an element name into an expression container or argument, or an AST fixture built with JSXMember in the wrong slot.

Common situations: swc plugins that rewrite JSX element names (e.g. renaming library prefixes); AST round-tripping tools; hand-written fixtures.

Related errors


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