@@ -278,12 +278,23 @@ module.exports = function (content) {
278278 var output = ''
279279 var parts = parse ( content , fileName , this . sourceMap )
280280 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+ }
281290
282291 // add requires for styles
283292 var cssModules
284293 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+ }
287298 parts . styles . forEach ( function ( style , i ) {
288299 // require style
289300 var requireString = style . src
@@ -301,7 +312,12 @@ module.exports = function (content) {
301312 var moduleName = ( style . module === true ) ? '$style' : style . module
302313 // setCssModule
303314 if ( moduleName ) {
304- cssModules = cssModules || { }
315+ if ( ! cssModules ) {
316+ cssModules = { }
317+ if ( needsHotReload ) {
318+ output += `var cssModules = {}\n`
319+ }
320+ }
305321 if ( moduleName in cssModules ) {
306322 loaderContext . emitError ( 'CSS module name "' + moduleName + '" is not unique!' )
307323 styleInjectionCode += invokeStyle ( requireString )
@@ -315,15 +331,36 @@ module.exports = function (content) {
315331 requireString += '.locals'
316332 }
317333
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+ }
319359 }
320360 } else {
321361 styleInjectionCode += invokeStyle ( requireString )
322362 }
323363 } )
324- if ( cssModules ) {
325- styleInjectionCode += ' return cssModuleTarget\n'
326- }
327364 styleInjectionCode += '}\n'
328365 output += styleInjectionCode
329366 }
@@ -435,11 +472,7 @@ module.exports = function (content) {
435472
436473 if ( ! query . inject ) {
437474 // hot reload
438- if (
439- ! isServer &&
440- ! isProduction &&
441- ( parts . script || parts . template )
442- ) {
475+ if ( needsHotReload ) {
443476 output +=
444477 '\n/* hot reload */\n' +
445478 'if (module.hot) {(function () {\n' +
@@ -452,9 +485,21 @@ module.exports = function (content) {
452485 ' hotAPI.createRecord("' + moduleId + '", Component.options)\n' +
453486 ' } else {\n'
454487 // 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+ }
455494 output +=
456495 ' hotAPI.reload("' + moduleId + '", Component.options)\n' +
457496 ' }\n'
497+ // dispose
498+ output +=
499+ ' module.hot.dispose(function (data) {\n' +
500+ ( cssModules ? ' data.cssModules = cssModules\n' : '' ) +
501+ ' disposed = true\n' +
502+ ' })\n'
458503 output += '})()}\n'
459504 }
460505 // final export
0 commit comments