swc-project/swc · error

module string names unimplemented

Error message

module string names unimplemented

What it means

The import-handling visitor in swc_bundler (import/mod.rs:372) rewrites re-export specifiers like `export { a as b } from './mod'`. When the original name is a string (`export { "a" as b } from './mod'`, ModuleExportName::Str for s.orig), it hits unimplemented!() because add_forced_ns_for needs a real Ident/Id. The panic aborts bundling.

Source

Thrown at crates/swc_bundler/src/bundler/import/mod.rs:372

            _ => return,
        };
        prop.ctxt = self.imported_idents.get(&obj.to_id()).copied().unwrap();

        *e = prop.into();
    }
}

impl<L, R> VisitMut for ImportHandler<'_, '_, L, R>
where
    L: Load,
    R: Resolve,
{
    noop_visit_mut_type!(fail);

    fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) {
        let orig = match &s.orig {
            ModuleExportName::Ident(ident) => ident,
            ModuleExportName::Str(..) => unimplemented!("module string names unimplemented"),
            #[cfg(swc_ast_unknown)]
            _ => panic!("unable to access unknown nodes"),
        };

        self.add_forced_ns_for(orig.to_id());

        match &mut s.exported {
            Some(ModuleExportName::Ident(exported)) => {
                // PR 3139 (https://github.com/swc-project/swc/pull/3139) removes the syntax context from any named exports from other sources.
                exported.ctxt = self.module_ctxt;
            }
            Some(ModuleExportName::Str(..)) => unimplemented!("module string names unimplemented"),
            #[cfg(swc_ast_unknown)]
            Some(_) => panic!("unable to access unknown nodes"),
            None => {
                let exported = Ident::new(orig.sym.clone(), orig.span, self.module_ctxt);
                s.exported = Some(ModuleExportName::Ident(exported));
            }

View on GitHub (pinned to d7d7434666)

Solutions

  1. Change the re-export to reference an identifier: `export { "foo-bar" as fooBar } from './dep'` -> rename the export inside './dep' to `fooBar` and re-export normally.
  2. If you don't control the dep, patch it (patch-package) or wrap it in a shim module that assigns the string-named import to an identifier before re-exporting.
  3. Pre-transform the graph with a custom SWC pass converting Str origs to Idents.
  4. Externalize the module from the bundle.

Example fix

// before
export { "foo-bar" as fooBar } from './dep';

// after (shim.ts)
import { "foo-bar" as tmp } from './dep';
export const fooBar = tmp;
Defensive patterns

Strategy: validation

Validate before calling

// Screen re-exports (export ... from) for string origs before bundling
use swc_ecma_ast::*;
for i in &m.body {
    if let ModuleItem::ModuleDecl(ModuleDecl::ExportNamedDecl(d)) = i {
        if d.src.is_some() {
            for s in &d.specifiers {
                if let ExportSpecifier::Named(n) = s {
                    if matches!(n.orig, ModuleExportName::Str(_)) {
                        anyhow::bail!("string orig in re-export from {}", d.src.as_ref().unwrap().value);
                    }
                }
            }
        }
    }
}

Type guard

fn reexport_has_string_orig(m: &swc_ecma_ast::Module) -> bool {
    use swc_ecma_ast::*;
    m.body.iter().any(|i| matches!(i,
        ModuleItem::ModuleDecl(ModuleDecl::ExportNamedDecl(d))
            if d.src.is_some() && d.specifiers.iter().any(|s| matches!(s,
                ExportSpecifier::Named(n) if matches!(n.orig, ModuleExportName::Str(_))))))
}

Try / catch

match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| bundler.bundle(part)?)) {
    Ok(v) => v,
    Err(p) if format!("{:?}", &p).contains("module string names") =>
        Err(anyhow::anyhow!("string-named re-export; rename at the exporting module")),
    Err(p) => std::panic::resume_unwind(p),
}

Prevention

When it happens

Trigger: A module re-exports a string-named binding: `export { "foo-bar" as fooBar } from './dep';`. The visitor visit_mut_export_named_specifier matches Str on s.orig and panics.

Common situations: Barrel files re-exporting generated exports with non-identifier names; dependencies whose ESM entry uses string re-export names; pipelines that transpile each file fine and only fail when the SWC bundler joins modules.

Related errors


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