Skip to content

Commit dfca85c

Browse files
fix: support hmr in css-module (webpack#519)
1 parent 1ffc393 commit dfca85c

File tree

6 files changed

+60
-13
lines changed

6 files changed

+60
-13
lines changed

src/loader.js

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,32 @@ import schema from './loader-options.json';
1717
const pluginName = 'mini-css-extract-plugin';
1818

1919
function hotLoader(content, context) {
20-
const accept = context.locals
21-
? ''
22-
: 'module.hot.accept(undefined, cssReload);';
23-
2420
return `${content}
2521
if(module.hot) {
26-
// ${Date.now()}
27-
var cssReload = require(${loaderUtils.stringifyRequest(
22+
var varifyLocal = function(a, b) {
23+
var key, idx = 0;
24+
for(key in a) {
25+
if(!b || a[key] !== b[key]) return false;
26+
idx++;
27+
}
28+
for(key in b) idx--;
29+
return idx === 0;
30+
};
31+
var update = require(${loaderUtils.stringifyRequest(
2832
context.context,
2933
path.join(__dirname, 'hmr/hotModuleReplacement.js')
3034
)})(module.id, ${JSON.stringify({
3135
...context.options,
3236
locals: !!context.locals,
3337
})});
34-
module.hot.dispose(cssReload);
35-
${accept}
38+
var cssReload = function () {
39+
var newContent = require(${context.localsPath});
40+
var localMatch = varifyLocal(content.locals, newContent.locals);
41+
if (!localMatch) throw new Error('Aborting CSS HMR due to changed css-modules locals.');
42+
update();
43+
};
44+
module.hot.accept(${context.localsPath}, function () { cssReload(); });
45+
module.hot.dispose(function () { update(); });
3646
}
3747
`;
3848
}
@@ -203,18 +213,24 @@ export function pitch(request) {
203213
return callback(e);
204214
}
205215

216+
const localsPath = loaderUtils.stringifyRequest(this, `!!${request}`);
206217
const esModule =
207218
typeof options.esModule !== 'undefined' ? options.esModule : false;
208219
const result = locals
209-
? `\n${esModule ? 'export default' : 'module.exports ='} ${JSON.stringify(
210-
locals
211-
)};`
220+
? `\nvar content = require(${localsPath});\n${
221+
esModule ? 'export default' : 'module.exports ='
222+
} content.locals || {};`
212223
: '';
213224

214225
let resultSource = `// extracted by ${pluginName}`;
215226

216227
resultSource += options.hmr
217-
? hotLoader(result, { context: this.context, options, locals })
228+
? hotLoader(result, {
229+
context: this.context,
230+
options,
231+
locals,
232+
localsPath,
233+
})
218234
: result;
219235

220236
return callback(null, resultSource);

test/cases/hmr/expected/webpack-4/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
/***/ (function(module, exports, __webpack_require__) {
9090

9191
// extracted by mini-css-extract-plugin
92-
if(false) { var cssReload; }
92+
if(false) { var cssReload, update, varifyLocal; }
9393

9494

9595
/***/ })

test/manual/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@
6363
<p>CrossOriginLoading Option: Must be red.</p>
6464
<p><button>Pressing this button</button> loads chunks with crossorigin attribute and should turn green.</p>
6565
</div>
66+
<div class="test hmr">
67+
<div class="hmr-a">With CSS Module, if locals didn't change, we can hmr update style</div>
68+
<div class="hmr-b">If locals changed, it will trigger a full reload of the page.</div>
69+
</div>
6670
<div class="errors"></div>
6771
<script async defer src="/dist/main.js"></script>
6872
</body>

test/manual/src/hmr-module.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* eslint-env browser */
2+
import style from './hmr-module.module.css';
3+
4+
export default function hmrBootstrap() {
5+
// should not log again if only css changed
6+
console.log('load');
7+
document.querySelector('.hmr-a').classList.add(style.a);
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
:local(.a) {
2+
/* change inner style can update with hmr */
3+
background-color: pink;
4+
height: 20px;
5+
}
6+
7+
:global(.hmr-b) {
8+
/* global className works fine in a css-module */
9+
background-color: skyblue;
10+
height: 20px;
11+
}
12+
13+
/* remove or add new local module would cause full reload */
14+
/* :local(.c) {
15+
background-color: khaki;
16+
} */

test/manual/src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import './initial.css';
66
import './simple.css';
77
import classes from './simple.module.css';
8+
import hmrBootstrap from './hmr-module';
89

910
console.log('___CLASSES__');
1011
console.log(classes);
@@ -69,3 +70,5 @@ makeButton('.crossorigin', () => {
6970
__webpack_public_path__ = originalPublicPath;
7071
return promise;
7172
});
73+
74+
hmrBootstrap();

0 commit comments

Comments
 (0)