swc-project/swc · error

codegen of `export default from 'foo';`

Error message

codegen of `export default from 'foo';`

What it means

swc's parser accepts the ESNext 'export default from' proposal syntax (`export default from './m';`, `export v from './m';`) and represents it as `ExportSpecifier::Default`. The code generator has no printer for that node — it is expected to be desugared by the `export_default_from` pass (swc_ecma_transforms_proposal) before printing; emitting it directly hits `unimplemented!`.

Source

Thrown at crates/swc_ecma_codegen/src/module_decls.rs:263

            space!(emitter);
            keyword!(emitter, "as");
            space!(emitter);
        }

        emit!(self.local);

        srcmap!(emitter, self, false);

        Ok(())
    }
}

#[node_impl]
impl MacroNode for ExportSpecifier {
    fn emit(&mut self, emitter: &mut Macro) -> Result {
        match self {
            ExportSpecifier::Default(..) => {
                unimplemented!("codegen of `export default from 'foo';`")
            }
            ExportSpecifier::Namespace(ref node) => emit!(node),
            ExportSpecifier::Named(ref node) => emit!(node),
            #[cfg(swc_ast_unknown)]
            _ => return Err(unknown_error()),
        }

        Ok(())
    }
}

#[node_impl]
impl MacroNode for ExportNamespaceSpecifier {
    fn emit(&mut self, emitter: &mut Macro) -> Result {
        emitter.emit_leading_comments_of_span(self.span(), false)?;

        srcmap!(emitter, self, true);

View on GitHub (pinned to d7d7434666)

Solutions

  1. Add the `export_default_from` pass to the pass chain before codegen
  2. Replace the proposal syntax with standard re-export syntax in the source
  3. Disable export-default-from in the parser syntax config so the input is rejected early instead of panicking at print time

Example fix

// before
export default from './mod';

// after (standard ES2015 re-export)
export { default as default } from './mod';
Defensive patterns

Strategy: validation

Validate before calling

// Rust: before codegen, walk the module for ExportSpecifier::Default
fn uses_export_default_from(m: &Module) -> bool {
    m.body.iter().any(|item| matches!(item, ModuleItem::ModuleDecl(ModuleDecl::ExportNamedDecl(d))
        if d.specifiers.iter().any(|s| matches!(s, ExportSpecifier::Default(..)))))
}

Type guard

fn is_printable_export(s: &ExportSpecifier) -> bool {
    !matches!(s, ExportSpecifier::Default(..))
}

Try / catch

// Rust: isolate third-party printing of untrusted ASTs
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| emitter.emit_module(&module)));
if result.is_err() { /* report and fall back to skipping re-print */ }

Prevention

When it happens

Trigger: Parse a module with export-default-from syntax enabled, then run the codegen without applying the `export_default_from` transform in between — custom pass pipelines, AST round-trippers, plugins that print raw AST.

Common situations: Hand-built pass lists that enable the parser syntax flag but omit the matching transform; formatters/pretty-printers that parse and print without lowering; pass sets that regress after an swc upgrade.

Related errors


AI-assisted analysis of swc-project/swc@d7d7434666 (2026-08-16). Data as JSON: /api/errors/b0dceeeaa11de852. Report an issue: GitHub.