fix(dev): serve assets emitted during lazy bundling on first load#22599
fix(dev): serve assets emitted during lazy bundling on first load#22599semimikoh wants to merge 1 commit into
Conversation
In bundled dev mode (`experimental.bundledDev`), `triggerLazyBundling` returned the lazy chunk's patch code as soon as `compileEntry` resolved. However, the assets emitted while compiling that chunk (e.g. images imported from JS or referenced by CSS `url()`) are only registered into `memoryFiles` by the `onOutput` callback, which can run after `compileEntry` resolves. The browser could therefore request those assets before they were available and receive a 404 on the first load, while a refresh succeeded. Await `ensureLatestBuildOutput()` after `compileEntry` so the emitted assets are flushed into `memoryFiles` before the patch code is sent. Closes vitejs#22596
@h-a-n-a I think we need a way to tell Vite about the additional assets generated by |
Sounds good. How would you like it to be used/consumed over the Vite side? Do you have an idea of how they interact in mind? |
I don't have an idea now. Maybe adding |
Description
Fixes #22596.
In bundled dev mode (
experimental.bundledDev), static asset imports such as a JS-imported image/SVG or a CSSbackground: url(...)failed to load the first time a lazily-loaded route/module was opened, and only loaded after a page refresh.Root cause
When a dynamically imported module is first requested,
triggerLazyBundlingcallsdevEngine.compileEntry()and returns the resulting lazy chunk patch code straight to the browser. The assets emitted while compiling that chunk are only registered intomemoryFilesby theonOutputcallback, which can fire aftercompileEntryresolves.So the sequence on first load is:
compileEntryresolves → patch code (referencing hashed asset URLs like/assets/foo-[hash].png) is sent to the browser.onOutputregisters the assets intomemoryFiles— but only now, after the request already missed.Because the hashed asset files exist only in
memoryFiles(not on disk), the request returns 404 on the first load. A refresh works because by thenonOutputhas registered the assets.Fix
Await
ensureLatestBuildOutput()aftercompileEntryso the emitted assets are flushed intomemoryFilesbefore the patch code is returned to the browser.Tests
Added a regression test in
playground/hmr-full-bundle-modethat lazily imports a module referencing both a JS-imported image and a CSSurl()asset, then asserts the image actually decodes (naturalWidth > 0) and that no asset request 404s on the first click.The fixture images use unique content so they are emitted as distinct hashed assets only when the lazy module is compiled (otherwise they would be deduplicated with an eagerly-bundled asset and never exercise the bug). Combined with the playground's existing
generateBundledelay plugin, the test fails deterministically without this change and passes with it.