Merge pull request #15586 from webpack/bugfix/chunk-load-during-hmr

fix egde case where a HMR chunk is incorrectly downloaded
This commit is contained in:
Tobias Koppers 2022-04-01 15:25:10 +02:00 committed by GitHub
commit c3f5897df9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 21 deletions

View File

@ -28,7 +28,8 @@ module.exports = function () {
var currentStatus = "idle";
// while downloading
var blockingPromises;
var blockingPromises = 0;
var blockingPromisesWaiting = [];
// The update info
var currentUpdateApplyHandlers;
@ -218,17 +219,28 @@ module.exports = function () {
return Promise.all(results);
}
function unblock() {
if (--blockingPromises === 0) {
setStatus("ready").then(function () {
if (blockingPromises === 0) {
var list = blockingPromisesWaiting;
blockingPromisesWaiting = [];
for (var i = 0; i < list.length; i++) {
list[i]();
}
}
});
}
}
function trackBlockingPromise(promise) {
switch (currentStatus) {
case "ready":
setStatus("prepare");
blockingPromises.push(promise);
waitForBlockingPromises(function () {
return setStatus("ready");
});
return promise;
/* fallthrough */
case "prepare":
blockingPromises.push(promise);
blockingPromises++;
promise.then(unblock, unblock);
return promise;
default:
return promise;
@ -236,11 +248,11 @@ module.exports = function () {
}
function waitForBlockingPromises(fn) {
if (blockingPromises.length === 0) return fn();
var blocker = blockingPromises;
blockingPromises = [];
return Promise.all(blocker).then(function () {
return waitForBlockingPromises(fn);
if (blockingPromises === 0) return fn();
return new Promise(function (resolve) {
blockingPromisesWaiting.push(function () {
resolve(fn());
});
});
}
@ -261,7 +273,6 @@ module.exports = function () {
return setStatus("prepare").then(function () {
var updatedModules = [];
blockingPromises = [];
currentUpdateApplyHandlers = [];
return Promise.all(
@ -298,7 +309,11 @@ module.exports = function () {
function hotApply(options) {
if (currentStatus !== "ready") {
return Promise.resolve().then(function () {
throw new Error("apply() is only allowed in ready status");
throw new Error(
"apply() is only allowed in ready status (state: " +
currentStatus +
")"
);
});
}
return internalApply(options);

View File

@ -443,15 +443,16 @@ module.exports = function () {
) {
promises.push($loadUpdateChunk$(chunkId, updatedModulesList));
currentUpdateChunks[chunkId] = true;
} else {
currentUpdateChunks[chunkId] = false;
}
});
if ($ensureChunkHandlers$) {
$ensureChunkHandlers$.$key$Hmr = function (chunkId, promises) {
if (
currentUpdateChunks &&
!$hasOwnProperty$(currentUpdateChunks, chunkId) &&
$hasOwnProperty$($installedChunks$, chunkId) &&
$installedChunks$[chunkId] !== undefined
$hasOwnProperty$(currentUpdateChunks, chunkId) &&
!currentUpdateChunks[chunkId]
) {
promises.push($loadUpdateChunk$(chunkId));
currentUpdateChunks[chunkId] = true;

View File

@ -286,8 +286,9 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
? Template.asString([
"var currentUpdatedModulesList;",
"var waitingUpdateResolves = {};",
"function loadUpdateChunk(chunkId) {",
"function loadUpdateChunk(chunkId, updatedModulesList) {",
Template.indent([
"currentUpdatedModulesList = updatedModulesList;",
`return new Promise(${runtimeTemplate.basicFunction(
"resolve, reject",
[

View File

@ -5,22 +5,27 @@ module.hot.accept("./file");
const asyncNext = () => {
return new Promise((resolve, reject) => {
NEXT((err, stats) => {
if(err) return reject(err);
if (err) return reject(err);
resolve(stats);
});
});
}
};
it("should download the missing update chunk on import", () => {
expect(value).toBe(1);
return asyncNext().then(() => {
return module.hot.check().then(() => {
return import("./chunk").then(chunk => {
return Promise.all([
import("./chunk"),
import("./unaffected-chunk")
]).then(([chunk, unaffectedChunk]) => {
expect(value).toBe(1);
expect(chunk.default).toBe(10);
expect(unaffectedChunk.default).toBe(10);
return module.hot.apply().then(() => {
expect(value).toBe(2);
expect(chunk.default).toBe(20);
expect(unaffectedChunk.default).toBe(10);
});
});
});

View File

@ -0,0 +1,5 @@
import value from "./unaffected-inner";
module.hot.accept("./unaffected-inner");
export { value as default };

View File

@ -0,0 +1 @@
export default 10;