swc-project/swc · error

module string names unimplemented

Error message

module string names unimplemented

What it means

When swc_bundler loads a module and records its imports for the dependency graph (load.rs:364), it converts ImportSpecifier::Named into its internal Specifier model. The `imported` field must resolve to an Id; a string name (`import { "a" as b } from './dep'`, ModuleExportName::Str) hits unimplemented!() and panics during load — earlier than most other string-name panics.

Source

Thrown at crates/swc_bundler/src/bundler/load.rs:364

                let src = Source {
                    is_loaded_synchronously: !is_dynamic,
                    is_unconditional,
                    module_id: id,
                    local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
                    export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
                    src: *decl.src,
                };
                files.push((src.clone(), file_name));

                // TODO: Handle rename
                let mut specifiers = Vec::new();
                for s in decl.specifiers {
                    match s {
                        ImportSpecifier::Named(s) => {
                            let imported = match s.imported {
                                Some(ModuleExportName::Ident(ident)) => Some(ident),
                                Some(ModuleExportName::Str(..)) => {
                                    unimplemented!("module string names unimplemented")
                                }
                                _ => None,
                            };
                            specifiers.push(Specifier::Specific {
                                local: s.local.into(),
                                alias: imported.map(From::from),
                            })
                        }
                        ImportSpecifier::Default(s) => specifiers.push(Specifier::Specific {
                            local: s.local.into(),
                            alias: Some(Id::new(atom!("default"), SyntaxContext::empty())),
                        }),
                        ImportSpecifier::Namespace(s) => {
                            specifiers.push(Specifier::Namespace {
                                local: s.local.into(),
                                all: forced_ns.contains(&src.src.value),
                            });
                        }

View on GitHub (pinned to d7d7434666)

Solutions

  1. Rewrite the import against an identifier export.
  2. Patch the exporting dependency to also export an identifier alias, then import that.
  3. Gate CI on a grep for `import { "` across src and node_modules so this fails fast with a clear message.
  4. Externalize the module or use a pre-bundle transform.

Example fix

// before
import { "legacy-key" as key } from './dep';

// after (dep adds: export const key = value;)
import { key } from './dep';
Defensive patterns

Strategy: validation

Validate before calling

// load.rs panics earliest: gate inside your Load impl
impl Load for ScreenedLoader {
    fn load(&self, f: &FileName) -> Result<ModuleRecord, anyhow::Error> {
        let rec = self.inner.load(f)?;
        let mut bad = StrImportScan::default();
        rec.module.visit_with(&mut bad);
        if bad.count > 0 {
            anyhow::bail!("{} contains {} string import name(s); swc_bundler load() will panic", f, bad.count);
        }
        Ok(rec)
    }
}

Type guard

fn load_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

// Loading happens inside Bundler::bundle; catch_unwind around the whole bundle call
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| bundler.bundle(part)?));
if let Err(p) = res {
    if format!("{:?}", &p).contains("module string names") {
        anyhow::bail!("a module in the graph uses string import names; see loader screen logs");
    }
    std::panic::resume_unwind(p);
}

Prevention

When it happens

Trigger: Any entry or dependency module contains `import { "string name" as local } from './x';`. The panic occurs in the load phase while building the import specifier list, before chunking even starts.

Common situations: Bundling a project whose deps use string import names; first run of a bundling pipeline against a package that was previously only transpiled; error location points at load.rs, confusing users who expect later stages.

Related errors


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