Repository navigation
Description
Bug report
What is the current behavior?
With output.clean: true, CleanPlugin keeps *.hot-update.js / *.hot-update.json of the previous compilation for a fixed 10 s (_10sec in lib/output/CleanPlugin.js, added in #15405 for #15206). When the next compilation takes longer than that, the update files of the previous one are deleted on its emit while a client may still be loading them.
Sequence with webpack-dev-server / webpack-dev-middleware:
- Compilation N+1 emits
main.<hashN>.hot-update.js(+runtime.<hashN>.hot-update.jswith a runtime chunk), kept untilemit(N+1) + 10 s. - The client receives
ok,hotCheckfetchesmain.<hashN>.hot-update.json— fine. - Before it requests the
.hot-update.jsfiles, compilation N+2 starts (second save, or any other invalidation). dev-middleware queues the requests until N+2 is done. - N+2 takes > 10 s. Its
CleanPluginpass removes the N+1 update files (ts < now). The queued requests get 404, or, withhistoryApiFallback,index.htmlas JS:
entry.86f6914295f12e02.hot-update.js:1 Uncaught SyntaxError: Unexpected token '<'
__webpack_require__.l
loadUpdateChunk
__webpack_require__.hmrC.jsonp
hotCheck
check @ dev-server.js
reloadApp @ index.js
Loading hot update chunk ... failed (missing)→[HMR] Cannot apply update. Need to do a full reload!→location.reload().
Rebuilds slower than 10 s are routine in css-in-js / large TS setups, so this is not an edge case there. With [contenthash] in dev filenames the runtime chunk's url map changes on nearly every rebuild, so the runtime chunk gets its own .hot-update.js every time, which widens the window further.
If the current behavior is a bug, please provide the steps to reproduce.
Failing unit test next to the existing "output.clean=true should keep 1 last update": erictheswift/webpack@f89a7c1 (test/unitCases/HotModuleReplacementPluginCompilation.unittest.js, "output.clean=true should keep the last update until a newer one is emitted, regardless of elapsed time").
It builds three times, waits past the 10 s window, builds a fourth time and expects the update emitted by the third build to still exist, because no newer update existed while the fourth build ran. On main the files are gone:
expect(hasFile(updates[2])).toBe(true)
Expected: true
Received: false
What is the expected behavior?
Retention of HMR update files should not depend on wall-clock time. Keep the update set of the previous compilation regardless of how long the next one takes, and drop it only once a newer update set has been emitted (files from compilation N go at the emit of N+2, not N+1). The existing test "should keep 1 last update" already describes that intent; only the 10 s timer contradicts it, and its final assertion (everything removed after 10 s) would need to change to "only the older set removed".
A count option (e.g. output.clean.keepHotUpdates) would also work, but any time-based default is wrong for builds that can exceed the constant.
Other relevant information:
webpack version: 5.108.4 (test reproduced on main at e95be18)
Node.js version: 24
Operating System: macOS
Additional tools: webpack-dev-server 6.0.0 / webpack-dev-middleware 8.0.3
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
fix: keep the last hot update files until a newer update is emitted (#…
fix: keep the last hot update files until a newer update is emitted