8000 fix: hmr reload with invalid link url (#402) · SenseITCS/mini-css-extract-plugin@30a19b0 · GitHub
Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 30a19b0

Browse files
fix: hmr reload with invalid link url (webpack-contrib#402)
1 parent ee9df43 commit 30a19b0

File tree

3 files changed

+331
-13
lines changed

3 files changed

+331
-13
lines changed

src/hmr/hotModuleReplacement.js

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,11 @@ function updateCss(el, url) {
120120

121121
newEl.href = `${url}?${Date.now()}`;
122122

123-
el.parentNode.appendChild(newEl);
123+
if (el.nextSibling) {
124+
el.parentNode.insertBefore(newEl, el.nextSibling);
125+
} else {
126+
el.parentNode.appendChild(newEl);
127+
}
124128
}
125129

126130
function getReloadUrl(href, src) {
@@ -160,6 +164,7 @@ function reloadStyle(src) {
160164

161165
if (url) {
162166
updateCss(el, url);
167+
163168
loaded = true;
164169
}
165170
});
@@ -182,18 +187,8 @@ function reloadAll() {
182187
function isUrlRequest(url) {
183188
// An URL is not an request if
184189

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)) {
190+
// It is not http or https
191+
if (!/^https?:/i.test(url)) {
197192
return false;
198193
}
199194

test/HMR.test.js

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
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+
links[1].dispatchEvent(getLoadEvent());
173+
174+
expect(links[1].isLoaded).toBe(true);
175+
expect(links[2].visited).toBeUndefined();
176+
177+
done();
178+
}, 100);
179+
});
180+
181+
it('should reloads with # link href', (done) => {
182+
document.head.innerHTML =
183+
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" href="#href" />';
184+
185+
const update = hotModuleReplacement('./src/style.css', {});
186+
187+
update();
188+
189+
setTimeout(() => {
190+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
191+
192+
const links = Array.prototype.slice.call(
193+
document.querySelectorAll('link')
194+
);
195+
196+
expect(links[0].visited).toBe(true);
197+
expect(document.head.innerHTML).toMatchSnapshot();
198+
199+
links[1].dispatchEvent(getLoadEvent());
200+
201+
expect(links[1].isLoaded).toBe(true);
202+
expect(links[2].visited).toBeUndefined();
203+
204+
done();
205+
}, 100);
206+
});
207+
208+
it('should reloads with link without href', (done) => {
209+
document.head.innerHTML =
210+
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" />';
211+
212+
const update = hotModuleReplacement('./src/style.css', {});
213+
214+
update();
215+
216+
setTimeout(() => {
217+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
218+
219+
const links = Array.prototype.slice.call(
220+
document.querySelectorAll('link')
221+
);
222+
223+
expect(links[0].visited).toBe(true);
224+
expect(document.head.innerHTML).toMatchSnapshot();
225+
226+
links[1].dispatchEvent(getLoadEvent());
227+
228+
expect(links[1].isLoaded).toBe(true);
229+
expect(links[2].visited).toBeUndefined();
230+
231+
done();
232+
}, 100);
233+
});
234+
235+
it('should reloads with absolute remove url', (done) => {
236+
document.head.innerHTML =
237+
'<link rel="stylesheet" href="/dist/main.css" /><link rel="stylesheet" href="http://dev.com/dist/main.css" />';
238+
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(getLoadEvent());
254+
255+
expect(links[1].isLoaded).toBe(true);
256+
expect(links[2].visited).toBeUndefined();
257+
258+
done();
259+
}, 100);
260+
});
261+
262+
it('should handle error event', (done) => {
263+
const update = hotModuleReplacement('./src/style.css', {});
264+
265+
update();
266+
267+
setTimeout(() => {
268+
expect(console.log.mock.calls[0][0]).toMatchSnapshot();
269+
270+
const links = Array.prototype.slice.call(
271+
document.querySelectorAll('link')
272+
);
273+
274+
expect(links[0].visited).toBe(true);
275+
expect(document.head.innerHTML).toMatchSnapshot();
276+
277+
links[1].dispatchEvent(getErrorEvent());
278+
279+
expect(links[1].isLoaded).toBe(true);
280+
281+
done();
282+
}, 100);
283+
});
284+
});

test/__snapshots__/HMR.test.js.snap

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"shortcut icon\\" href=\\"#href\\">"`;
10+
11+
exports[`HMR should reloads with absolute remove url 1`] = `"[HMR] css reload %s"`;
12+
13+
exports[`HMR should reloads with absolute remove url 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"stylesheet\\" href=\\"http://dev.com/dist/main.css\\">"`;
14+
15+
exports[`HMR should reloads with link without href 1`] = `"[HMR] css reload %s"`;
16+
17+
exports[`HMR should reloads with link without href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"shortcut icon\\">"`;
18+
19+
exports[`HMR should reloads with locals 1`] = `"[HMR] Detected local css modules. Reload all css"`;
20+
21+
exports[`HMR should reloads with locals 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
22+
23+
exports[`HMR should reloads with non http/https link href 1`] = `"[HMR] css reload %s"`;
24+
25+
exports[`HMR should reloads with non http/https link href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"shortcut icon\\" href=\\"data:;base64,=\\">"`;
26+
27+
exports[`HMR should reloads with reloadAll option 1`] = `"[HMR] Reload all css"`;
28+
29+
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\\">"`;
30+
31+
exports[`HMR should works 1`] = `"[HMR] css reload %s"`;
32+
33+
exports[`HMR should works 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 1`] = `"[HMR] css reload %s"`;
36+
37+
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\\">"`;
38+
39+
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)