{"record":{"id":"9ac3e1d6b551b434","repo":"swc-project/swc","slug":"node-resolver-supports-only-files","errorCode":null,"errorMessage":"node-resolver supports only files","messagePattern":"node-resolver supports only files","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/swc_ecma_loader/src/resolvers/node.rs","lineNumber":441,"sourceCode":"            \"Resolving {} from {:#?} for {:#?}\",\n            module_specifier, base, self.target_env\n        );\n\n        let path = Path::new(module_specifier);\n        if path.is_absolute() {\n            if let Ok(file) = self\n                .resolve_as_file(path)\n                .or_else(|_| self.resolve_as_directory(path, false))\n            {\n                if let Ok(file) = self.wrap(file) {\n                    return Ok(file);\n                }\n            }\n        }\n\n        let base = match base {\n            FileName::Real(v) => v,\n            _ => bail!(\"node-resolver supports only files\"),\n        };\n\n        let base_dir = if base.is_file() {\n            let cwd = &Path::new(\".\");\n            base.parent().unwrap_or(cwd)\n        } else {\n            base\n        };\n\n        // Handle module references for the `browser` package config\n        // before we map aliases.\n        if let TargetEnv::Browser = self.target_env {\n            if let Some(pkg_base) = find_package_root(base) {\n                if let Some(item) = BROWSER_CACHE.get(&pkg_base) {\n                    let value = item.value();\n                    if value.module_ignores.contains(module_specifier) {\n                        return Ok(FileName::Custom(module_specifier.into()));\n                    }","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_ecma_loader/src/resolvers/node.rs#L423-L459","documentation":"NodeResolver::resolve takes a `base` FileName and uses it to anchor relative specifier resolution. Only FileName::Real is meaningful for filesystem lookups; when the caller passes any other variant (FileName::Custom, FileName::Anon, FileName::Internal, etc.) the resolver refuses to proceed and bails 'node-resolver supports only files'. The same restriction applies to the alternative `resolve_as_import`-style entry just above, which requires an existing file path.","triggerScenarios":"Calling NodeResolver::resolve with a FileName produced by another tool that wraps sources in FileName::Custom (e.g. virtual file systems in editors, plugin pipelines, or bundler loaders that tag inputs), or passing FileName::Anon for in-memory sources.","commonSituations":"Embedding swc in an environment (IDE plugin, playground, custom bundler) where the current file name is a virtual/custom value; migrating from an older resolver API that accepted any FileName; wiring a loader chain where an upstream transform replaced the Real filename.","solutions":["Convert the virtual name into a real on-disk path (write to a temp/cache dir and pass FileName::Real) before invoking NodeResolver","If resolution against real files is impossible, implement or use a resolver that understands your virtual FS instead of NodeResolver","Check the pipeline step that produced FileName::Custom and preserve FileName::Real through it"],"exampleFix":"// before\nlet resolved = node_resolver.resolve(base_file_name /* FileName::Custom(\"virt:main.ts\") */, \"./dep\");\n\n// after: map virtual files to real paths first\nlet real_base = materialize(&base_file_name); // writes to cache dir, returns PathBuf\nlet resolved = node_resolver.resolve(FileName::Real(real_base), \"./dep\");","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"use swc_common::FileName;\nfn realBase(f: &FileName) -> Option<&std::path::Path> {\n    match f {\n        FileName::Real(p) => Some(p),\n        _ => None, // Custom/Anon cannot anchor NodeResolver\n    }\n}","tryCatchPattern":"match node_resolver.resolve(base, spec) {\n    Ok(f) => Ok(f),\n    Err(e) if e.to_string().contains(\"node-resolver supports only files\") => materialize_to_real_path(base).and_then(|b| node_resolver.resolve(FileName::Real(b), spec)),\n    Err(e) => Err(e),\n}","preventionTips":["Propagate FileName::Real through your whole loader chain; tag virtual files only at the edges","Materialize virtual sources to a real cache dir before filesystem-based resolution","Write unit tests asserting the base filename variant before calling NodeResolver"],"tags":["module-resolution","node-resolver","filename-type","virtual-fs","swc-ecma-loader"],"backgroundTag":"unsupported-filename-type","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}