Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled HMR updates should cause a page reload #2676

Merged
merged 8 commits into from Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/core/integration-tests/test/hmr.js
Expand Up @@ -392,6 +392,45 @@ describe('hmr', function() {
assert.deepEqual(outputs, [3, 10]);
});

it('should bubble up HMR events to a page reload', async function() {
await ncp(
path.join(__dirname, '/integration/hmr'),
path.join(__dirname, '/input')
);

b = bundler(path.join(__dirname, '/input/index.js'), {
watch: true,
hmr: true
});
let bundle = await b.bundle();

let outputs = [];
let ctx = await run(
bundle,
{
output(o) {
outputs.push(o);
}
},
{require: false}
);
let spy = sinon.spy(ctx.location, 'reload');

await sleep(50);
assert.deepEqual(outputs, [3]);
assert(spy.notCalled);

await sleep(100);
fs.writeFile(
path.join(__dirname, '/input/local.js'),
'exports.a = 5; exports.b = 5;'
);

await nextEvent(b, 'bundled');
assert.deepEqual(outputs, [3]);
assert(spy.calledOnce);
});

it('should log emitted errors and show an error overlay', async function() {
await ncp(
path.join(__dirname, '/integration/commonjs'),
Expand Down
35 changes: 25 additions & 10 deletions packages/core/parcel-bundler/src/builtins/hmr-runtime.js
Expand Up @@ -29,21 +29,36 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') {
var ws = new WebSocket(protocol + '://' + hostname + ':' + process.env.HMR_PORT + '/');
ws.onmessage = function(event) {
updatedAssets = {};

var data = JSON.parse(event.data);

if (data.type === 'update') {
console.clear();

data.assets.forEach(function (asset) {
hmrApply(global.parcelRequire, asset);
var handled = false;
data.assets.forEach(function(asset) {
handled =
handled ||
(global.parcelRequire.cache[asset.id] &&
(Boolean(global.parcelRequire.cache[asset.id].hot._acceptCallbacks.length) ||
Boolean(global.parcelRequire.cache[asset.id].hot._disposeCallbacks.length)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite enough. You need to traverse upward from the asset to the root asset and check if any of the parents accept the update as well as the asset itself. Basically, we need to split hmrAccept up into two functions. One that traverses upward and checks if an update should be accepted, but without actually re-executing them. It can also keep track of all of the assets that need to be re-executed in a list. Then, if an update is accepted, go through the list we tracked earlier, and re-execute them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: extracted the actual execution into hmrAcceptRun and kept track of them in assetsToAccept

);
});

data.assets.forEach(function (asset) {
if (!asset.isNew) {
hmrAccept(global.parcelRequire, asset.id);
}
});
if(handled){
console.clear();

data.assets.forEach(function (asset) {
hmrApply(global.parcelRequire, asset);
});

data.assets.forEach(function (asset) {
if (!asset.isNew) {
hmrAccept(global.parcelRequire, asset.id);
}
});
} else {
window.location.reload();
}
}

if (data.type === 'reload') {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test-utils/src/utils.js
Expand Up @@ -230,7 +230,7 @@ function prepareBrowserContext(bundle, globals) {
document: fakeDocument,
WebSocket,
console,
location: {hostname: 'localhost'},
location: {hostname: 'localhost', reload() {}},
fetch(url) {
return Promise.resolve({
arrayBuffer() {
Expand Down