Skip to content

[Bug]: HMR fails with 'Module not found' for tsconfig wildcard path aliases #12753

@devin-ai-integration

Description

@devin-ai-integration

System Info

  • rspack-resolver version: 0.6.7
  • Rsbuild version: 1.7.1

Details

When using a wildcard tsconfig path alias ("*": ["./src/*"]), HMR intermittently fails with "Module not found" errors. The error persists until dev server restart.

Root Cause Analysis

After investigating the rspack and rspack-resolver source code, I found the issue is in how the resolver cache is cleared during HMR:

  1. During HMR, plugin_driver.clear_cache() is called (rebuild.rs:58)
  2. This calls resolver_factory.clear_cache() which clears BOTH the file paths cache AND the tsconfig cache
  3. The module graph is recovered from the previous compilation (rebuild.rs:87-89)
  4. When modules are re-resolved with an empty tsconfig cache, the tsconfig.json needs to be re-parsed
  5. This can cause intermittent resolution failures, particularly with wildcard path aliases

Proposed Fix

Add a new method clear_cache_without_tsconfig() to rspack-resolver that only clears the file system paths cache but preserves the tsconfig cache. Then use this method during HMR instead of the full clear_cache().

The tsconfig.json file doesn't change during HMR (it's a configuration file), so there's no need to re-parse it on every hot reload.

Patch for rspack-resolver

diff --git a/src/cache.rs b/src/cache.rs
index 8d54f36..9f771cd 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -42,6 +42,10 @@ impl<Fs: Send + Sync + FileSystem> Cache<Fs> {
     self.tsconfigs.clear();
   }
 
+  pub fn clear_without_tsconfig(&self) {
+    self.paths.clear();
+  }
+
   pub fn value(&self, path: &Path) -> CachedPath {
     let hash = {
       let mut hasher = FxHasher::default();
diff --git a/src/lib.rs b/src/lib.rs
index e42a24d..6fcb16e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -185,6 +185,21 @@ impl<Fs: FileSystem + Send + Sync> ResolverGeneric<Fs> {
     }
   }
 
+  /// Clear the underlying cache without clearing tsconfig cache.
+  ///
+  /// This is useful for HMR (Hot Module Replacement) scenarios where tsconfig.json
+  /// doesn't change during development, but file system state may change.
+  /// Preserving the tsconfig cache avoids re-parsing tsconfig.json on every HMR update,
+  /// which can cause intermittent module resolution failures with tsconfig path aliases.
+  pub fn clear_cache_without_tsconfig(&self) {
+    self.cache.clear_without_tsconfig();
+    #[cfg(feature = "yarn_pnp")
+    {
+      self.pnp_manifest_content_cache.clear();
+      self.pnp_manifest_path_cache.clear();
+    }
+  }
+
   /// Resolve \`specifier\` at an absolute path to a \`directory\`.

Configuration

{
  "compilerOptions": {
    "paths": {
      "*": ["./src/*"]
    }
  }
}

Behavior

  • Initial build: succeeds
  • After HMR: intermittently fails with "Module not found"
  • Page refresh: does NOT recover
  • Dev server restart: recovers

Link to Devin run

https://app.devin.ai/sessions/cf71fa99683946c79f26f1f325951338

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions