swc-project/swc · error

module string names unimplemented

Error message

module string names unimplemented

What it means

swc_bundler renames identifiers that collide with contextual keywords before merging (keywords.rs). Its visit_mut_export_named_specifier only handles identifier origs; `export { "a" as b }` (ModuleExportName::Str) reaches unimplemented!() at keywords.rs:63 because there is no Ident to rename. The panic aborts the bundle.

Source

Thrown at crates/swc_bundler/src/bundler/keywords.rs:63

        c.class.visit_mut_with(self);
        if let Some(renamed) = self.renamed(&c.ident) {
            c.ident = renamed;
        }
    }

    fn visit_mut_class_prop(&mut self, n: &mut ClassProp) {
        if n.key.is_computed() {
            n.key.visit_mut_with(self);
        }

        n.decorators.visit_mut_with(self);
        n.value.visit_mut_with(self);
    }

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

    fn visit_mut_expr(&mut self, n: &mut Expr) {
        if let Expr::Ident(n) = n {
            if let Some(renamed) = self.renamed(n) {
                *n = renamed;
            }
            return;
        }

        n.visit_mut_children_with(self);
    }

View on GitHub (pinned to d7d7434666)

Solutions

  1. Replace the string orig with the identifier it refers to at the definition site and re-export normally.
  2. Introduce a shim with identifier bindings for the string-named exports and import from the shim.
  3. Scan sources and dependencies for `export { "` / `as "` patterns and normalize them.
  4. Pre-transform the graph with a custom SWC pass before handing it to the bundler.

Example fix

// before
export { "default" as def } from './mod';

// after
export { default as def } from './mod';
Defensive patterns

Strategy: validation

Validate before calling

// Same universal screen: any ModuleExportName::Str anywhere dooms the bundle
use swc_ecma_visit::{Visit, VisitWith};
use swc_ecma_ast::ModuleExportName;
struct AnyStr(pub bool);
impl Visit for AnyStr {
    fn visit_module_export_name(&mut self, n: &ModuleExportName) {
        if matches!(n, ModuleExportName::Str(_)) { self.0 = true; }
        n.visit_children_with(self);
    }
}
let mut v = AnyStr(false);
m.visit_with(&mut v);
anyhow::ensure!(!v.0, "keyword-rename pass will panic on string module names");

Type guard

fn keyword_pass_safe(m: &swc_ecma_ast::Module) -> bool {
    use swc_ecma_visit::{Visit, VisitWith};
    use swc_ecma_ast::ModuleExportName;
    struct F(bool); impl Visit for F {
        fn visit_module_export_name(&mut self, n: &ModuleExportName) {
            if matches!(n, ModuleExportName::Str(_)) { self.0 = false; }
            n.visit_children_with(self);
        }
    }
    let mut f = F(true); m.visit_with(&mut f); f.0
}

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 module name hit keyword-rename; normalize to identifiers")),
    Err(p) => std::panic::resume_unwind(p),
}

Prevention

When it happens

Trigger: A module in the graph contains `export { "string" as alias };` (or the re-export form) and enters the keyword-rename pass; matching on n.orig hits Str and panics.

Common situations: Bundling code that exports names colliding with keywords via string aliases; generated code using string origs; failures that occur even when no keyword collision exists, because the match arms are evaluated for every named export.

Related errors


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