@@ -58,7 +58,7 @@ module.exports = function (content) {
5858 var bubleOptions = hasBuble && options . buble ? '?' + JSON . stringify ( options . buble ) : ''
5959 var defaultLoaders = {
6060 html : templateCompilerPath + '?id=' + moduleId ,
61- css : styleLoaderPath + '!css-loader' + ( needCssSourceMap ? '?sourceMap' : '' ) ,
61+ css : ( isServer ? '' : styleLoaderPath + '!' ) + ' css-loader' + ( needCssSourceMap ? '?sourceMap' : '' ) ,
6262 js : hasBuble ? ( 'buble-loader' + bubleOptions ) : hasBabel ? 'babel-loader' : ''
6363 }
6464
@@ -77,7 +77,7 @@ module.exports = function (content) {
7777 // disable all configuration loaders
7878 '!!' +
7979 // get loader string for pre-processors
80- getLoaderString ( type , part , scoped ) +
80+ getLoaderString ( type , part , index , scoped ) +
8181 // select the corresponding part from the vue file
8282 getSelectorString ( type , index || 0 ) +
8383 // the url to the actual vuefile
@@ -94,17 +94,42 @@ module.exports = function (content) {
9494 function getRequireForImportString ( type , impt , scoped ) {
9595 return loaderUtils . stringifyRequest ( loaderContext ,
9696 '!!' +
97- getLoaderString ( type , impt , scoped ) +
97+ getLoaderString ( type , impt , - 1 , scoped ) +
9898 impt . src
9999 )
100100 }
101101
102- function getLoaderString ( type , part , scoped ) {
102+ function addCssModulesToLoader ( loader , part , index ) {
103+ if ( ! part . module ) return loader
104+ var option = options . cssModules || { }
105+ return loader . replace ( / ( (?: ^ | ! ) c s s (?: - l o a d e r ) ? ) ( \? [ ^ ! ] * ) ? / , function ( m , $1 , $2 ) {
106+ // $1: !css-loader
107+ // $2: ?a=b
108+ var query = loaderUtils . parseQuery ( $2 )
109+ query . modules = true
110+ query . importLoaders = true
111+ query . localIdentName = option . localIdentName || '[hash:base64]'
112+ if ( index !== - 1 ) {
113+ // Note:
114+ // Class name is generated according to its filename.
115+ // Different <style> tags in the same .vue file may generate same names.
116+ // Append `_[index]` to class name to avoid this.
117+ query . localIdentName += '_' + index
118+ }
119+ return $1 + '?' + JSON . stringify ( query )
120+ } )
121+ }
122+
123+ function getLoaderString ( type , part , index , scoped ) {
103124 var lang = part . lang || defaultLang [ type ]
104125 var loader = loaders [ lang ]
105126 var rewriter = type === 'styles' ? styleRewriter + ( scoped ? '&scoped=true!' : '!' ) : ''
106127 var injectString = ( type === 'script' && query . inject ) ? 'inject!' : ''
107128 if ( loader !== undefined ) {
129+ // add css modules
130+ if ( type === 'styles' ) {
131+ loader = addCssModulesToLoader ( loader , part , index )
132+ }
108133 // inject rewriter before css/html loader for
109134 // extractTextPlugin use cases
110135 if ( rewriterInjectRE . test ( loader ) ) {
@@ -121,7 +146,8 @@ module.exports = function (content) {
121146 case 'template' :
122147 return defaultLoaders . html + '!' + templateLoaderPath + '?raw&engine=' + lang + '!'
123148 case 'styles' :
124- return defaultLoaders . css + '!' + rewriter + lang + '!'
149+ loader = addCssModulesToLoader ( defaultLoaders . css , part , index )
150+ return loader + '!' + rewriter + lang + '!'
125151 case 'script' :
126152 return injectString + lang + '!'
127153 }
@@ -146,13 +172,42 @@ module.exports = function (content) {
146172 var hasScoped = parts . styles . some ( function ( s ) { return s . scoped } )
147173 var output = 'var __vue_exports__, __vue_options__\n'
148174
175+ // css modules
176+ output += 'var __vue_styles__ = {}\n'
177+ var cssModules = { }
178+
149179 // add requires for styles
150- if ( ! isServer && parts . styles . length ) {
180+ if ( parts . styles . length ) {
151181 output += '\n/* styles */\n'
152182 parts . styles . forEach ( function ( style , i ) {
153- output += style . src
183+ var moduleName = ( style . module === true ) ? '$style' : style . module
184+
185+ // require style
186+ if ( isServer && ! moduleName ) return
187+ var requireString = style . src
154188 ? getRequireForImport ( 'styles' , style , style . scoped )
155189 : getRequire ( 'styles' , style , i , style . scoped )
190+
191+ // setCssModule
192+ if ( moduleName ) {
193+ if ( moduleName in cssModules ) {
194+ loaderContext . emitError ( 'CSS module name "' + moduleName + '" is not unique!' )
195+ output += requireString
196+ } else {
197+ cssModules [ moduleName ] = true
198+
199+ // `style-loader` exposes the name-to-hash map directly
200+ // `css-loader` exposes it in `.locals`
201+ // We drop `style-loader` in SSR, and add `.locals` here.
202+ if ( isServer ) {
203+ requireString += '.locals'
204+ }
205+
206+ output += '__vue_styles__["' + moduleName + '"] = ' + requireString + '\n'
207+ }
208+ } else {
209+ output += requireString
210+ }
156211 } )
157212 }
158213
@@ -205,6 +260,16 @@ module.exports = function (content) {
205260 exports += '__vue_options__._scopeId = "' + moduleId + '"\n'
206261 }
207262
263+ if ( Object . keys ( cssModules ) . length ) {
264+ // inject style modules as computed properties
265+ exports +=
266+ 'if (!__vue_options__.computed) __vue_options__.computed = {}\n' +
267+ 'Object.keys(__vue_styles__).forEach(function (key) {\n' +
268+ 'var module = __vue_styles__[key]\n' +
269+ '__vue_options__.computed[key] = function () { return module }\n' +
270+ '})\n'
271+ }
272+
208273 if ( ! query . inject ) {
209274 output += exports
210275 // hot reload
0 commit comments