@@ -278,12 +278,23 @@ module.exports = function (content) {
278
278
var output = ''
279
279
var parts = parse ( content , fileName , this . sourceMap )
280
280
var hasScoped = parts . styles . some ( function ( s ) { return s . scoped } )
281
+ var needsHotReload = (
282
+ ! isServer &&
283
+ ! isProduction &&
284
+ ( parts . script || parts . template )
285
+ )
286
+
287
+ if ( needsHotReload ) {
288
+ output += 'var disposed = false\n'
289
+ }
281
290
282
291
// add requires for styles
283
292
var cssModules
284
293
if ( parts . styles . length ) {
285
- var styleInjectionCode =
286
- 'function injectStyle (cssModuleTarget, ssrContext) {\n'
294
+ var styleInjectionCode = 'function injectStyle (ssrContext) {\n'
295
+ if ( needsHotReload ) {
296
+ styleInjectionCode += ` if (disposed) return\n`
297
+ }
287
298
parts . styles . forEach ( function ( style , i ) {
288
299
// require style
289
300
var requireString = style . src
@@ -301,7 +312,12 @@ module.exports = function (content) {
301
312
var moduleName = ( style . module === true ) ? '$style' : style . module
302
313
// setCssModule
303
314
if ( moduleName ) {
304
- cssModules = cssModules || { }
315
+ if ( ! cssModules ) {
316
+ cssModules = { }
317
+ if ( needsHotReload ) {
318
+ output += `var cssModules = {}\n`
319
+ }
320
+ }
305
321
if ( moduleName in cssModules ) {
306
322
loaderContext . emitError ( 'CSS module name "' + moduleName + '" is not unique!' )
307
323
styleInjectionCode += invokeStyle ( requireString )
@@ -315,15 +331,36 @@ module.exports = function (content) {
315
331
requireString += '.locals'
316
332
}
317
333
318
- styleInjectionCode += invokeStyle ( 'cssModuleTarget["' + moduleName + '"] = ' + requireString )
334
+ if ( ! needsHotReload ) {
335
+ styleInjectionCode += invokeStyle ( 'this["' + moduleName + '"] = ' + requireString )
336
+ } else {
337
+ // handle hot reload for CSS modules.
338
+ // we store the exported locals in an object and proxy to it by
339
+ // defining getters inside component instances' lifecycle hook.
340
+ styleInjectionCode +=
341
+ invokeStyle ( `cssModules["${ moduleName } "] = ${ requireString } ` ) +
342
+ `Object.defineProperty(this, "${ moduleName } ", { get: function () { return cssModules["${ moduleName } "] }})\n`
343
+
344
+ var requirePath = getRequireString ( 'styles' , style , i , style . scoped )
345
+ output +=
346
+ `module.hot && module.hot.accept([${ requirePath } ], function () {\n` +
347
+ // 1. check if style has been injected
348
+ ` var oldLocals = cssModules["${ moduleName } "]\n` +
349
+ ` if (!oldLocals) return\n` +
350
+ // 2. re-import (side effect: updates the <style>)
351
+ ` var newLocals = ${ requireString } \n` +
352
+ // 3. compare new and old locals to see if selectors changed
353
+ ` if (JSON.stringify(newLocals) === JSON.stringify(oldLocals)) return\n` +
354
+ // 4. locals changed. Update and force re-render.
355
+ ` cssModules["${ moduleName } "] = newLocals\n` +
356
+ ` require("${ hotReloadAPIPath } ").rerender("${ moduleId } ")\n` +
357
+ `})\n`
358
+ }
319
359
}
320
360
} else {
321
361
styleInjectionCode += invokeStyle ( requireString )
322
362
}
323
363
} )
324
- if ( cssModules ) {
325
- styleInjectionCode += ' return cssModuleTarget\n'
326
- }
327
364
styleInjectionCode += '}\n'
328
365
output += styleInjectionCode
329
366
}
@@ -435,11 +472,7 @@ module.exports = function (content) {
435
472
436
473
if ( ! query . inject ) {
437
474
// hot reload
438
- if (
439
- ! isServer &&
440
- ! isProduction &&
441
- ( parts . script || parts . template )
442
- ) {
475
+ if ( needsHotReload ) {
443
476
output +=
444
477
'\n/* hot reload */\n' +
445
478
'if (module.hot) {(function () {\n' +
@@ -452,9 +485,21 @@ module.exports = function (content) {
452
485
' hotAPI.createRecord("' + moduleId + '", Component.options)\n' +
453
486
' } else {\n'
454
487
// update
488
+ if ( cssModules ) {
489
+ output +=
490
+ ' if (module.hot.data.cssModules && Object.keys(module.hot.data.cssModules) !== Object.keys(cssModules)) {\n' +
491
+ ' delete Component.options._Ctor\n' +
492
+ ' }\n'
493
+ }
455
494
output +=
456
495
' hotAPI.reload("' + moduleId + '", Component.options)\n' +
457
496
' }\n'
497
+ // dispose
498
+ output +=
499
+ ' module.hot.dispose(function (data) {\n' +
500
+ ( cssModules ? ' data.cssModules = cssModules\n' : '' ) +
501
+ ' disposed = true\n' +
502
+ ' })\n'
458
503
output += '})()}\n'
459
504
}
460
505
// final export
0 commit comments