Skip to content

Commit 03be38b

Browse files
feat(hotLoading): Addressing some issues in HMR
CSS modules should have better HMR. Updates and renames were also done that appear to have gotton lost Fixed up CSS reloadAll Dont assume CSS modules be default Also fixing css loading order issues that appear during HMR Pulling in a handful of fixes from webpack
1 parent 7198177 commit 03be38b

File tree

4 files changed

+791
-659
lines changed

4 files changed

+791
-659
lines changed

src/hotLoader.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
const path = require('path');
2+
23
const loaderUtils = require('loader-utils');
34

45
const defaultOptions = {
5-
fileMap: '{fileName}',
6-
cssModules: true,
6+
fileMap: '{fileName}',
77
};
88

9-
module.exports = function (content) {
10-
this.cacheable();
11-
const options = Object.assign(
9+
module.exports = function(content) {
10+
this.cacheable();
11+
const options = Object.assign(
1212
{},
1313
defaultOptions,
14-
loaderUtils.getOptions(this),
14+
loaderUtils.getOptions(this)
1515
);
1616

17-
const accept = options.cssModules ? '' : 'module.hot.accept(undefined, cssReload);';
18-
return content + `
17+
const accept = options.cssModules
18+
? ''
19+
: 'module.hot.accept(undefined, cssReload);';
20+
return `${content}
1921
if(module.hot) {
2022
// ${Date.now()}
21-
var cssReload = require(${loaderUtils.stringifyRequest(this, path.join(__dirname, 'hotModuleReplacement.js'))})(module.id, ${JSON.stringify(options)});
23+
var cssReload = require(${loaderUtils.stringifyRequest(
24+
this,
25+
path.join(__dirname, 'hotModuleReplacement.js')
26+
)})(module.id, ${JSON.stringify(options)});
2227
module.hot.dispose(cssReload);
2328
${accept};
2429
}

src/hotModuleReplacement.js

Lines changed: 97 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,121 +6,128 @@ const debounce = require('lodash/debounce');
66
const noDocument = typeof document === 'undefined';
77
const forEach = Array.prototype.forEach;
88

9-
const noop = function () {};
10-
11-
const getCurrentScriptUrl = function (moduleId) {
12-
let src = srcByModuleId[moduleId];
13-
14-
if (!src) {
15-
if (document.currentScript) {
16-
src = document.currentScript.src;
17-
} else {
18-
const scripts = document.getElementsByTagName('script');
19-
const lastScriptTag = scripts[scripts.length - 1];
20-
21-
if (lastScriptTag) {
22-
src = lastScriptTag.src;
23-
}
9+
const noop = function() {};
10+
11+
const getCurrentScriptUrl = function(moduleId) {
12+
let src = srcByModuleId[moduleId];
13+
14+
if (!src) {
15+
if (document.currentScript) {
16+
src = document.currentScript.src;
17+
} else {
18+
const scripts = document.getElementsByTagName('script');
19+
const lastScriptTag = scripts[scripts.length - 1];
20+
21+
if (lastScriptTag) {
22+
src = lastScriptTag.src;
23+
}
24+
}
25+
srcByModuleId[moduleId] = src;
2426
}
25-
srcByModuleId[moduleId] = src;
26-
}
27-
28-
return function (fileMap) {
29-
const splitResult = /([^\\/]+)\.js$/.exec(src);
30-
const filename = splitResult && splitResult[1];
31-
if (!filename) {
32-
return [src.replace('.js', '.css')];
33-
}
34-
return fileMap.split(',').map(function (mapRule) {
35-
const reg = new RegExp(filename + '\\.js$', 'g');
36-
return normalizeUrl(src.replace(reg, mapRule.replace(/{fileName}/g, filename) + '.css'), { stripWWW: false });
37-
});
38-
};
27+
28+
return function(fileMap) {
29+
const splitResult = /([^\\/]+)\.js$/.exec(src);
30+
const filename = splitResult && splitResult[1];
31+
if (!filename) {
32+
return [src.replace('.js', '.css')];
33+
}
34+
return fileMap.split(',').map((mapRule) => {
35+
const reg = new RegExp(`${filename}\\.js$`, 'g');
36+
return normalizeUrl(
37+
src.replace(
38+
reg,
39+
`${mapRule.replace(/{fileName}/g, filename)}.css`
40+
),
41+
{ stripWWW: false }
42+
);
43+
});
44+
};
3945
};
4046

4147
function updateCss(el, url) {
42-
if (!url) {
43-
url = el.href.split('?')[0];
44-
}
45-
if (el.isLoaded === false) {
48+
if (!url) {
49+
url = el.href.split('?')[0];
50+
}
51+
if (el.isLoaded === false) {
4652
// We seem to be about to replace a css link that hasn't loaded yet.
4753
// We're probably changing the same file more than once.
48-
return;
49-
}
50-
if (!url || !(url.indexOf('.css') > -1)) return;
54+
return;
55+
}
56+
if (!url || !(url.indexOf('.css') > -1)) return;
5157

52-
el.visited = true;
53-
const newEl = el.cloneNode();
58+
el.visited = true;
59+
const newEl = el.cloneNode();
5460

55-
newEl.isLoaded = false;
61+
newEl.isLoaded = false;
5662

57-
newEl.addEventListener('load', function () {
58-
newEl.isLoaded = true;
59-
el.parentNode.removeChild(el);
60-
});
63+
newEl.addEventListener('load', () => {
64+
newEl.isLoaded = true;
65+
el.parentNode.removeChild(el);
66+
});
6167

62-
newEl.addEventListener('error', function () {
63-
newEl.isLoaded = true;
64-
el.parentNode.removeChild(el);
65-
});
68+
newEl.addEventListener('error', () => {
69+
newEl.isLoaded = true;
70+
el.parentNode.removeChild(el);
71+
});
6672

67-
newEl.href = url + '?' + Date.now();
68-
el.parentNode.appendChild(newEl);
73+
newEl.href = `${url}?${Date.now()}`;
74+
el.parentNode.appendChild(newEl);
6975
}
7076

7177
function getReloadUrl(href, src) {
72-
href = normalizeUrl(href, { stripWWW: false });
73-
let ret;
74-
src.some(function (url) {
75-
if (href.indexOf(src) > -1) {
76-
ret = url;
77-
}
78-
});
79-
return ret;
78+
href = normalizeUrl(href, { stripWWW: false });
79+
let ret;
80+
// eslint-disable-next-line array-callback-return
81+
src.some((url) => {
82+
if (href.indexOf(src) > -1) {
83+
ret = url;
84+
}
85+
});
86+
return ret;
8087
}
8188

8289
function reloadStyle(src) {
83-
const elements = document.querySelectorAll('link');
84-
let loaded = false;
90+
const elements = document.querySelectorAll('link');
91+
let loaded = false;
8592

86-
forEach.call(elements, function (el) {
87-
if (el.visited === true) return;
93+
forEach.call(elements, (el) => {
94+
if (el.visited === true) return;
8895

89-
const url = getReloadUrl(el.href, src);
90-
if (url) {
91-
updateCss(el, url);
92-
loaded = true;
93-
}
94-
});
96+
const url = getReloadUrl(el.href, src);
97+
if (url) {
98+
updateCss(el, url);
99+
loaded = true;
100+
}
101+
});
95102

96-
return loaded;
103+
return loaded;
97104
}
98105

99106
function reloadAll() {
100-
const elements = document.querySelectorAll('link');
101-
forEach.call(elements, function (el) {
102-
if (el.visited === true) return;
103-
updateCss(el);
104-
});
107+
const elements = document.querySelectorAll('link');
108+
forEach.call(elements, (el) => {
109+
if (el.visited === true) return;
110+
updateCss(el);
111+
});
105112
}
106113

107-
module.exports = function (moduleId, options) {
108-
if (noDocument) {
109-
return noop;
110-
}
111-
112-
const getScriptSrc = getCurrentScriptUrl(moduleId);
113-
114-
function update() {
115-
const src = getScriptSrc(options.fileMap);
116-
const reloaded = reloadStyle(src);
117-
if (reloaded && !options.reloadAll) {
118-
console.log('[HMR] css reload %s', src.join(' '));
119-
} else {
120-
console.log('[HMR] Reload all css');
121-
reloadAll();
114+
module.exports = function(moduleId, options) {
115+
if (noDocument) {
116+
return noop;
117+
}
118+
119+
const getScriptSrc = getCurrentScriptUrl(moduleId);
120+
121+
function update() {
122+
const src = getScriptSrc(options.fileMap);
123+
const reloaded = reloadStyle(src);
124+
if (reloaded && !options.reloadAll) {
125+
console.log('[HMR] css reload %s', src.join(' '));
126+
} else {
127+
console.log('[HMR] Reload all css');
128+
reloadAll();
129+
}
122130
}
123-
}
124131

125-
return debounce(update, 10);
132+
return debounce(update, 10);
126133
};

0 commit comments

Comments
 (0)