Skip to content

Commit

Permalink
Fix HMR bugs (#7514)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Jan 2, 2022
1 parent 5746ab1 commit 6956600
Show file tree
Hide file tree
Showing 18 changed files with 258 additions and 105 deletions.
219 changes: 127 additions & 92 deletions packages/core/integration-tests/test/hmr.js
Expand Up @@ -79,10 +79,17 @@ describe('hmr', function () {
ws = await openSocket('ws://localhost:' + port);

let outputs = [];
let reloaded = false;
await run(bundleGraph, {
output(o) {
outputs.push(o);
},
location: {
reload() {
reloaded = true;
outputs = [];
},
},
});

for (let update of [].concat(updates)) {
Expand All @@ -94,13 +101,16 @@ describe('hmr', function () {
fsUpdates[f],
);
}
}

await nextWSMessage(nullthrows(ws));
await sleep(100);
await nextWSMessage(nullthrows(ws));
await sleep(100);
}

// Fixup the prototypes so that strict assertions work
return JSON.parse(JSON.stringify(outputs));
return {
outputs: JSON.parse(JSON.stringify(outputs)),
reloaded,
};
}

afterEach(async () => {
Expand Down Expand Up @@ -330,7 +340,7 @@ describe('hmr', function () {
});

it('should support self accepting', async function () {
let outputs = await testHMRClient('hmr-accept-self', outputs => {
let {outputs} = await testHMRClient('hmr-accept-self', outputs => {
assert.deepStrictEqual(outputs, [
['other', 1],
['local', 1],
Expand All @@ -352,7 +362,7 @@ describe('hmr', function () {
});

it('should bubble through parents', async function () {
let outputs = await testHMRClient('hmr-bubble', outputs => {
let {outputs} = await testHMRClient('hmr-bubble', outputs => {
assert.deepStrictEqual(outputs, [
['other', 1],
['local', 1],
Expand All @@ -375,7 +385,7 @@ describe('hmr', function () {
});

it('should call dispose callbacks', async function () {
let outputs = await testHMRClient('hmr-dispose', outputs => {
let {outputs} = await testHMRClient('hmr-dispose', outputs => {
assert.deepStrictEqual(outputs, [
['eval:other', 1, null],
['eval:local', 1, null],
Expand Down Expand Up @@ -417,7 +427,7 @@ module.hot.dispose((data) => {
});

it('should work with circular dependencies', async function () {
let outputs = await testHMRClient('hmr-circular', outputs => {
let {outputs} = await testHMRClient('hmr-circular', outputs => {
assert.deepEqual(outputs, [3]);

return {
Expand All @@ -429,6 +439,115 @@ module.hot.dispose((data) => {
assert.deepEqual(outputs, [3, 10]);
});

it('should reload if not accepted', async function () {
let {reloaded} = await testHMRClient('hmr-reload', outputs => {
assert.deepEqual(outputs, [3]);
return {
'local.js': 'exports.a = 5; exports.b = 5;',
};
});

assert(reloaded);
});

it('should reload when modifying the entry', async function () {
let {reloaded} = await testHMRClient('hmr-reload', outputs => {
assert.deepEqual(outputs, [3]);
return {
'index.js': 'output(5)',
};
});

assert(reloaded);
});

it('should work with multiple parents', async function () {
let {outputs} = await testHMRClient('hmr-multiple-parents', outputs => {
assert.deepEqual(outputs, ['a: fn1 b: fn2']);
return {
'fn2.js': 'export function fn2() { return "UPDATED"; }',
};
});

assert.deepEqual(outputs, ['a: fn1 b: fn2', 'a: fn1 b: UPDATED']);
});

it('should reload if only one parent accepts', async function () {
let {reloaded} = await testHMRClient(
'hmr-multiple-parents-reload',
outputs => {
assert.deepEqual(outputs, ['a: fn1', 'b: fn2']);
return {
'fn2.js': 'export function fn2() { return "UPDATED"; }',
};
},
);

assert(reloaded);
});

it('should work across bundles', async function () {
let {reloaded} = await testHMRClient('hmr-dynamic', outputs => {
assert.deepEqual(outputs, [3]);
return {
'local.js': 'exports.a = 5; exports.b = 5;',
};
});

// assert.deepEqual(outputs, [3, 10]);
assert(reloaded); // TODO: this should eventually not reload...
});

it('should work with urls', async function () {
let search;
let {outputs} = await testHMRClient('hmr-url', outputs => {
assert.equal(outputs.length, 1);
let url = new URL(outputs[0]);
assert(/test\.[0-9a-f]+\.txt/, url.pathname);
assert(!isNaN(url.search.slice(1)));
search = url.search;
return {
'test.txt': 'yo',
};
});

assert.equal(outputs.length, 2);
let url = new URL(outputs[1]);
assert(/test\.[0-9a-f]+\.txt/, url.pathname);
assert(!isNaN(url.search.slice(1)));
assert.notEqual(url.search, search);
});

it('should clean up orphaned assets when deleting a dependency', async function () {
let search;
let {outputs} = await testHMRClient('hmr-url', [
outputs => {
assert.equal(outputs.length, 1);
let url = new URL(outputs[0]);
assert(/test\.[0-9a-f]+\.txt/, url.pathname);
assert(!isNaN(url.search.slice(1)));
search = url.search;
return {
'index.js': 'output("yo"); module.hot.accept();',
};
},
outputs => {
assert.equal(outputs.length, 2);
assert.equal(outputs[1], 'yo');
return {
'index.js':
'output(new URL("test.txt", import.meta.url)); module.hot.accept();',
};
},
]);

assert.equal(outputs.length, 3);
let url = new URL(outputs[2]);
assert(/test\.[0-9a-f]+\.txt/, url.pathname);
assert(!isNaN(url.search.slice(1)));
assert.notEqual(url.search, search);
});

/*
it.skip('should accept HMR updates in the runtime after an initial error', async function() {
await fs.mkdirp(path.join(__dirname, '/input'));
Expand Down Expand Up @@ -530,90 +649,6 @@ module.hot.dispose((data) => {
]);
});
it.skip('should work across bundles', async function() {
await ncp(
path.join(__dirname, '/integration/hmr-dynamic'),
path.join(__dirname, '/input'),
);
let port = await getPort();
let b = await bundle(path.join(__dirname, '/input/index.js'), {
hmrOptions: {
https: false,
port,
host: 'localhost',
},
env: {
HMR_HOSTNAME: 'localhost',
HMR_PORT: port,
},
watch: true,
});
let outputs = [];
await run(b, {
output(o) {
outputs.push(o);
},
});
await sleep(50);
assert.deepEqual(outputs, [3]);
let ws = new WebSocket('ws://localhost:' + port);
await sleep(50);
fs.writeFile(
path.join(__dirname, '/input/local.js'),
'exports.a = 5; exports.b = 5;',
);
await nextWSMessage(ws);
await sleep(50);
assert.deepEqual(outputs, [3, 10]);
});
it.skip('should bubble up HMR events to a page reload', async function() {
await ncp(
path.join(__dirname, '/integration/hmr-reload'),
path.join(__dirname, '/input'),
);
let 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.skip('should trigger a page reload when a new bundle is created', async function() {
await ncp(
path.join(__dirname, '/integration/hmr-new-bundle'),
Expand Down
@@ -1,3 +1,4 @@
import * as styles from "./index.module.css";
import React from 'react';

const Hello = () => <div classNames={styles.hello}>hello</div>;
export const Hello = () => <div classNames={styles.hello}>hello</div>;
@@ -0,0 +1,5 @@
{
"dependencies": {
"react": "^16"
}
}
@@ -0,0 +1,4 @@
import {fn1} from './utils';

output('a: ' + fn1());
module.hot.accept();
@@ -0,0 +1,3 @@
import {fn2} from './utils';

output('b: ' + fn2());
@@ -0,0 +1,3 @@
export function fn1() {
return 'fn1';
}
@@ -0,0 +1,3 @@
export function fn2() {
return 'fn2';
}
@@ -0,0 +1,2 @@
import './a';
import './b';
@@ -0,0 +1,2 @@
export * from './fn1';
export * from './fn2';
@@ -0,0 +1,5 @@
import {fn1} from './utils';

export function a() {
return 'a: ' + fn1();
}
@@ -0,0 +1,5 @@
import {fn2} from './utils';

export function b() {
return 'b: ' + fn2();
}
@@ -0,0 +1,3 @@
export function fn1() {
return 'fn1';
}
@@ -0,0 +1,3 @@
export function fn2() {
return 'fn2';
}
@@ -0,0 +1,6 @@
import {a} from './a';
import {b} from './b';

output(a() + ' ' + b());

module.hot.accept();
@@ -0,0 +1,2 @@
export * from './fn1';
export * from './fn2';
@@ -0,0 +1,3 @@
let url = new URL('test.txt', import.meta.url);
output(url);
module.hot.accept();
@@ -0,0 +1 @@
hi

0 comments on commit 6956600

Please sign in to comment.