Conversation
Webpack can restore a module whose resource is already the _virtual_ path from cache. That file exists only in memory, so a later process ENOENTs unless the empty virtual file is written again before the read.
buildModule must skip paths that exist on disk, because the prefix has no trailing separator and would otherwise shadow files such as _virtual_helper.js with an empty virtual module.
Skip writeModule only when _virtualFiles already has the path. Treating __vfsModules as proof the file exists was the old bug.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughWebpack now restores missing virtual-module placeholders during webpack module building and resolution. Tests cover cached resolution, cleared in-memory files, repeated compiler runs, and collisions with real files. ChangesWebpack virtual-module restoration
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to No actionable merge-blocking risk is established from the available evidence. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Some tools did not complete. Review the errors below. 🔧 ESLint
src/webpack/index.tsESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. test/unit-tests/webpack/virtual-cache.test.tsESLint skipped: the matched ESLint configuration already failed (missing-dependency). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CodeRabbit's pre-merge docstring coverage check requires 80% on functions touched by the diff.
|
Hi @sxzz, I'd love to get your review on this if you have a moment. It fixes the |
Fixes: #618
On webpack, unplugin can serve modules that are not real files, for example icon imports in unplugin-icons. It creates an empty placeholder in memory and points the module at a made-up
_virtual_path. The plugin'sloadhook supplies the real source.Current behaviour
The placeholder is only created in
resolveId. Webpack stores the_virtual_path in its on-disk cache. The placeholder never reaches disk, so a new process starts without it.When that cache is reused (a
next devrestart, or a CI or Vercel build that reuses.next/cache), webpack already knows the path and skipsresolveId. It then opens the path and fails withENOENT.This is the unplugin side of unplugin/unplugin-icons#206. That issue goes away once a release of this package is published and unplugin-icons picks it up.
Related: unjs/unplugin#32
Expected behaviour
A cached virtual module still builds in a new process. The placeholder is there before webpack opens the path, even when
resolveIdnever runs.This change
compilation.hooks.buildModulecreates the same empty placeholder if this process does not already have it.resolveIduses the same helper, so a missing placeholder is also recreated there.We skip the write if the path already exists on disk or in memory. The
_virtual_prefix has no trailing separator, so a real file such as_virtual_helper.jswould otherwise be overwritten.The virtual filesystem is still installed only for plugins that define
resolveId. Rspack and the other bundler adapters are unchanged.Tests
pnpm exec vitest run test/unit-tests/webpack/virtual-cache.test.ts— a normal virtual load; a build where a fake resolver returns the_virtual_path soresolveIdis skipped; the same build after the in-memory map is cleared; and an on-disk_virtual_helper.jsthat must keep its contentspnpm exec vitest run test/unit-tests/webpack test/unit-tests/virtual-idpnpm exec eslint src/webpack/index.ts test/unit-tests/webpack/virtual-cache.test.tsThe skipped-
resolveIdcase fails on currentmainwithENOENTon_virtual_~demoand passes with this change. It fakes the skip with a resolver; it does not drive webpack's filesystem cache.For the cache path itself, we started and stopped a Next.js webpack app with the filesystem cache enabled. The
ENOENTshowed up, and it did not come back with this change applied.Summary by CodeRabbit
Summary by CodeRabbit
Bug Fixes
Tests