Skip to content

Commit e1179bf

Browse files
committed
fix egde case where a HMR chunk is incorrectly downloaded when loading a unchanged chunk during HMR downloading
1 parent 86a8bd9 commit e1179bf

6 files changed

Lines changed: 49 additions & 21 deletions

File tree

lib/hmr/HotModuleReplacement.runtime.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ module.exports = function () {
2828
var currentStatus = "idle";
2929

3030
// while downloading
31-
var blockingPromises;
31+
var blockingPromises = 0;
32+
var blockingPromisesWaiting = [];
3233

3334
// The update info
3435
var currentUpdateApplyHandlers;
@@ -218,29 +219,40 @@ module.exports = function () {
218219
return Promise.all(results);
219220
}
220221

222+
function unblock() {
223+
if (--blockingPromises === 0) {
224+
setStatus("ready").then(function () {
225+
if (blockingPromises === 0) {
226+
var list = blockingPromisesWaiting;
227+
blockingPromisesWaiting = [];
228+
for (var i = 0; i < list.length; i++) {
229+
list[i]();
230+
}
231+
}
232+
});
233+
}
234+
}
235+
221236
function trackBlockingPromise(promise) {
222237
switch (currentStatus) {
223238
case "ready":
224239
setStatus("prepare");
225-
blockingPromises.push(promise);
226-
waitForBlockingPromises(function () {
227-
return setStatus("ready");
228-
});
229-
return promise;
240+
/* fallthrough */
230241
case "prepare":
231-
blockingPromises.push(promise);
242+
blockingPromises++;
243+
promise.then(unblock, unblock);
232244
return promise;
233245
default:
234246
return promise;
235247
}
236248
}
237249

238250
function waitForBlockingPromises(fn) {
239-
if (blockingPromises.length === 0) return fn();
240-
var blocker = blockingPromises;
241-
blockingPromises = [];
242-
return Promise.all(blocker).then(function () {
243-
return waitForBlockingPromises(fn);
251+
if (blockingPromises === 0) return fn();
252+
return new Promise(function (resolve) {
253+
blockingPromisesWaiting.push(function () {
254+
resolve(fn());
255+
});
244256
});
245257
}
246258

@@ -261,7 +273,6 @@ module.exports = function () {
261273

262274
return setStatus("prepare").then(function () {
263275
var updatedModules = [];
264-
blockingPromises = [];
265276
currentUpdateApplyHandlers = [];
266277

267278
return Promise.all(
@@ -298,7 +309,11 @@ module.exports = function () {
298309
function hotApply(options) {
299310
if (currentStatus !== "ready") {
300311
return Promise.resolve().then(function () {
301-
throw new Error("apply() is only allowed in ready status");
312+
throw new Error(
313+
"apply() is only allowed in ready status (state: " +
314+
currentStatus +
315+
")"
316+
);
302317
});
303318
}
304319
return internalApply(options);

lib/hmr/JavascriptHotModuleReplacement.runtime.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,15 +443,16 @@ module.exports = function () {
443443
) {
444444
promises.push($loadUpdateChunk$(chunkId, updatedModulesList));
445445
currentUpdateChunks[chunkId] = true;
446+
} else {
447+
currentUpdateChunks[chunkId] = false;
446448
}
447449
});
448450
if ($ensureChunkHandlers$) {
449451
$ensureChunkHandlers$.$key$Hmr = function (chunkId, promises) {
450452
if (
451453
currentUpdateChunks &&
452-
!$hasOwnProperty$(currentUpdateChunks, chunkId) &&
453-
$hasOwnProperty$($installedChunks$, chunkId) &&
454-
$installedChunks$[chunkId] !== undefined
454+
$hasOwnProperty$(currentUpdateChunks, chunkId) &&
455+
!currentUpdateChunks[chunkId]
455456
) {
456457
promises.push($loadUpdateChunk$(chunkId));
457458
currentUpdateChunks[chunkId] = true;

lib/web/JsonpChunkLoadingRuntimeModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,9 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
286286
? Template.asString([
287287
"var currentUpdatedModulesList;",
288288
"var waitingUpdateResolves = {};",
289-
"function loadUpdateChunk(chunkId) {",
289+
"function loadUpdateChunk(chunkId, updatedModulesList) {",
290290
Template.indent([
291+
"currentUpdatedModulesList = updatedModulesList;",
291292
`return new Promise(${runtimeTemplate.basicFunction(
292293
"resolve, reject",
293294
[

test/hotCases/runtime/import-after-download/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@ module.hot.accept("./file");
55
const asyncNext = () => {
66
return new Promise((resolve, reject) => {
77
NEXT((err, stats) => {
8-
if(err) return reject(err);
8+
if (err) return reject(err);
99
resolve(stats);
1010
});
1111
});
12-
}
12+
};
1313

1414
it("should download the missing update chunk on import", () => {
1515
expect(value).toBe(1);
1616
return asyncNext().then(() => {
1717
return module.hot.check().then(() => {
18-
return import("./chunk").then(chunk => {
18+
return Promise.all([
19+
import("./chunk"),
20+
import("./unaffected-chunk")
21+
]).then(([chunk, unaffectedChunk]) => {
1922
expect(value).toBe(1);
2023
expect(chunk.default).toBe(10);
24+
expect(unaffectedChunk.default).toBe(10);
2125
return module.hot.apply().then(() => {
2226
expect(value).toBe(2);
2327
expect(chunk.default).toBe(20);
28+
expect(unaffectedChunk.default).toBe(10);
2429
});
2530
});
2631
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import value from "./unaffected-inner";
2+
3+
module.hot.accept("./unaffected-inner");
4+
5+
export { value as default };
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 10;

0 commit comments

Comments
 (0)