Skip to content

Commit 643545c

Browse files
fix: hmr reload with invalid link url
1 parent ee9df43 commit 643545c

File tree

3 files changed

+298
-12
lines changed

3 files changed

+298
-12
lines changed

src/hmr/hotModuleReplacement.js

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ function reloadStyle(src) {
160160

161161
if (url) {
162162
updateCss(el, url);
163+
163164
loaded = true;
164165
}
165166
});
@@ -182,18 +183,8 @@ function reloadAll() {
182183
function isUrlRequest(url) {
183184
// An URL is not an request if
184185

185-
// 1. It's an absolute url
186-
if (/^[a-z][a-z0-9+.-]*:/i.test(url)) {
187-
return false;
188-
}
189-
190-
// 2. It's a protocol-relative
191-
if (/^\/\//.test(url)) {
192-
return false;
193-
}
194-
195-
// 3. Its a `#` link
196-
if (/^#/.test(url)) {
186+
// It is not http or https
187+
if (!/^https?:/i.test(url)) {
197188
return false;
198189
}
199190

test/HMR.test.js

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/* eslint-env browser */
2+
/* eslint-disable no-console */
3+
4+
import hotModuleReplacement from '../src/hmr/hotModuleReplacement';
5+
6+
function getLoadEvent() {
7+
const event = document.createEvent('Event');
8+
9+
event.initEvent('load', false, false);
10+
11+
return event;
12+
}
13+
14+
function getErrorEvent() {
15+
const event = document.createEvent('Event');
16+
17+
event.initEvent('error', false, false);
18+
19+
return event;
20+
}
21+
22+
describe('HMR', () => {
23+
let consoleMock = null;
24+
25+
beforeEach(() => {
26+
consoleMock = jest.spyOn(console, 'log').mockImplementation(() => () => {});
27+
28+
jest.spyOn(Date, 'now').mockImplementation(() => 1479427200000);
29+
30+
document.head.innerHTML = '<link rel="stylesheet" href="/dist/main.css" />';
31+
document.body.innerHTML = '<script src="/dist/main.js"></script>';
32+
});
33+
34+
afterEach(() => {
35+
consoleMock.mockClear();
36+
});
37+
38+
it('should works', (done) => {
39+
const update = hotModuleReplacement('./src/style.css', {});
40+
41+
update();
42+
43+
setTimeout(() => {
44+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
45+
46+
const links = Array.prototype.slice.call(
47+
document.querySelectorAll('link')
48+
);
49+
50+
expect(links[0].visited).toBe(true);
51+
expect(document.head.innerHTML).toMatchSnapshot();
52+
53+
links[1].dispatchEvent(getLoadEvent());
54+
55+
expect(links[1].isLoaded).toBe(true);
56+
57+
done();
58+
}, 100);
59+
});
60+
61+
it('should works with multiple updates', (done) => {
62+
const update = hotModuleReplacement('./src/style.css', {});
63+
64+
update();
65+
66+
setTimeout(() => {
67+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
68+
69+
const links = Array.prototype.slice.call(
70+
document.querySelectorAll('link')
71+
);
72+
73+
expect(links[0].visited).toBe(true);
74+
expect(document.head.innerHTML).toMatchSnapshot();
75+
76+
links[1].dispatchEvent(getLoadEvent());
77+
78+
expect(links[1].isLoaded).toBe(true);
79+
80+
jest.spyOn(Date, 'now').mockImplementation(() => 1479427200001);
81+
82+
const update2 = hotModuleReplacement('./src/style.css', {});
83+
84+
update2();
85+
86+
setTimeout(() => {
87+
const links2 = Array.prototype.slice.call(
88+
document.querySelectorAll('link')
89+
);
90+
91+
expect(links2[0].visited).toBe(true);
92+
expect(links2[0].isLoaded).toBe(true);
93+
expect(document.head.innerHTML).toMatchSnapshot();
94+
95+
links2[1].dispatchEvent(getLoadEvent());
96+
97+
expect(links2[1].isLoaded).toBe(true);
98+
99+
done();
100+
}, 100);
101+
}, 100);
102+
});
103+
104+
it('should reloads with locals', (done) => {
105+
const update = hotModuleReplacement('./src/style.css', {
106+
locals: { foo: 'bar' },
107+
});
108+
109+
update();
110+
111+
setTimeout(() => {
112+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
113+
114+
const links = Array.prototype.slice.call(
115+
document.querySelectorAll('link')
116+
);
117+
118+
expect(links[0].visited).toBe(true);
119+
expect(document.head.innerHTML).toMatchSnapshot();
120+
121+
links[1].dispatchEvent(getLoadEvent());
122+
123+
expect(links[1].isLoaded).toBe(true);
124+
125+
done();
126+
}, 100);
127+
});
128+
129+
it('should reloads with reloadAll option', (done) => {
130+
const update = hotModuleReplacement('./src/style.css', {
131+
reloadAll: true,
132+
});
133+
134+
update();
135+
136+
setTimeout(() => {
137+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
138+
139+
const links = Array.prototype.slice.call(
140+
document.querySelectorAll('link')
141+
);
142+
143+
expect(links[0].visited).toBe(true);
144+
expect(document.head.innerHTML).toMatchSnapshot();
145+
146+
links[1].dispatchEvent(getLoadEvent());
147+
148+
expect(links[1].isLoaded).toBe(true);
149+
150+
done();
151+
}, 100);
152+
});
153+
154+
it('should reloads with non http/https link href', (done) => {
155+
document.head.innerHTML =
156+
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" href="data:;base64,=" />';
157+
158+
const update = hotModuleReplacement('./src/style.css', {});
159+
160+
update();
161+
162+
setTimeout(() => {
163+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
164+
165+
const links = Array.prototype.slice.call(
166+
document.querySelectorAll('link')
167+
);
168+
169+
expect(links[0].visited).toBe(true);
170+
expect(document.head.innerHTML).toMatchSnapshot();
171+
172+
expect(links[1].visited).toBeUndefined();
173+
174+
links[2].dispatchEvent(getLoadEvent());
175+
176+
expect(links[2].isLoaded).toBe(true);
177+
178+
done();
179+
}, 100);
180+
});
181+
182+
it('should reloads with # link href', (done) => {
183+
document.head.innerHTML =
184+
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" href="#href" />';
185+
186+
const update = hotModuleReplacement('./src/style.css', {});
187+
188+
update();
189+
190+
setTimeout(() => {
191+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
192+
193+
const links = Array.prototype.slice.call(
194+
document.querySelectorAll('link')
195+
);
196+
197+
expect(links[0].visited).toBe(true);
198+
expect(document.head.innerHTML).toMatchSnapshot();
199+
200+
expect(links[1].visited).toBeUndefined();
201+
202+
links[2].dispatchEvent(getLoadEvent());
203+
204+
expect(links[2].isLoaded).toBe(true);
205+
206+
done();
207+
}, 100);
208+
});
209+
210+
it('should reloads with link without href', (done) => {
211+
document.head.innerHTML =
212+
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" />';
213+
214+
const update = hotModuleReplacement('./src/style.css', {});
215+
216+
update();
217+
218+
setTimeout(() => {
219+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
220+
221+
const links = Array.prototype.slice.call(
222+
document.querySelectorAll('link')
223+
);
224+
225+
expect(links[0].visited).toBe(true);
226+
expect(document.head.innerHTML).toMatchSnapshot();
227+
228+
expect(links[1].visited).toBeUndefined();
229+
230+
links[2].dispatchEvent(getLoadEvent());
231+
232+
expect(links[2].isLoaded).toBe(true);
233+
234+
done();
235+
}, 100);
236+
});
237+
238+
it('should handle error event', (done) => {
239+
const update = hotModuleReplacement('./src/style.css', {});
240+
241+
update();
242+
243+
setTimeout(() => {
244+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
245+
246+
const links = Array.prototype.slice.call(
247+
document.querySelectorAll('link')
248+
);
249+
250+
expect(links[0].visited).toBe(true);
251+
expect(document.head.innerHTML).toMatchSnapshot();
252+
253+
links[1].dispatchEvent(getErrorEvent());
254+
255+
expect(links[1].isLoaded).toBe(true);
256+
257+
done();
258+
}, 100);
259+
});
260+
});

test/__snapshots__/HMR.test.js.snap

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`HMR should handle error event 1`] = `"[HMR] css reload %s"`;
4+
5+
exports[`HMR should handle error event 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
6+
7+
exports[`HMR should reloads with # link href 1`] = `"[HMR] css reload %s"`;
8+
9+
exports[`HMR should reloads with # link href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"shortcut icon\\" href=\\"#href\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
10+
11+
exports[`HMR should reloads with link without href 1`] = `"[HMR] css reload %s"`;
12+
13+
exports[`HMR should reloads with link without href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"shortcut icon\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
14+
15+
exports[`HMR should reloads with locals 1`] = `"[HMR] Detected local css modules. Reload all css"`;
16+
17+
exports[`HMR should reloads with locals 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
18+
19+
exports[`HMR should reloads with non http/https link href 1`] = `"[HMR] css reload %s"`;
20+
21+
exports[`HMR should reloads with non http/https link href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"shortcut icon\\" href=\\"data:;base64,=\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
22+
23+
exports[`HMR should reloads with reloadAll option 1`] = `"[HMR] Reload all css"`;
24+
25+
exports[`HMR should reloads with reloadAll option 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
26+
27+
exports[`HMR should works 1`] = `"[HMR] css reload %s"`;
28+
29+
exports[`HMR should works 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
30+
31+
exports[`HMR should works with multiple updates 1`] = `"[HMR] css reload %s"`;
32+
33+
exports[`HMR should works with multiple updates 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
34+
35+
exports[`HMR should works with multiple updates 3`] = `"<link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200001\\">"`;

0 commit comments

Comments
 (0)