@@ -7,13 +7,19 @@ const localByDefault = require('postcss-modules-local-by-default');
77const extractImports = require ( 'postcss-modules-extract-imports' ) ;
88const modulesScope = require ( 'postcss-modules-scope' ) ;
99const modulesValues = require ( 'postcss-modules-values' ) ;
10- const loaderUtils = require ( 'loader-utils' ) ;
10+ const {
11+ getOptions,
12+ isUrlRequest,
13+ urlToRequest,
14+ getRemainingRequest,
15+ getCurrentRequest,
16+ stringifyRequest,
17+ } = require ( 'loader-utils' ) ;
1118
1219const { importParser, icssParser, urlParser } = require ( './plugins' ) ;
1320const {
1421 getLocalIdent,
1522 getImportPrefix,
16- placeholderImportItemReplacer,
1723 compileExports,
1824 placholderRegExps,
1925} = require ( './utils' ) ;
@@ -22,7 +28,7 @@ const CssSyntaxError = require('./CssSyntaxError');
2228
2329module . exports = function loader ( content , map ) {
2430 const callback = this . async ( ) ;
25- const options = loaderUtils . getOptions ( this ) || { } ;
31+ const options = getOptions ( this ) || { } ;
2632 const sourceMap = options . sourceMap || false ;
2733
2834 /* eslint-disable no-param-reassign */
@@ -43,40 +49,38 @@ module.exports = function loader(content, map) {
4349 }
4450 /* eslint-enable no-param-reassign */
4551
52+ const parserOptions = { } ;
53+ const resolveImport = options . import !== false ;
54+ const resolveUrl = options . url !== false ;
4655 const loaderContext = this ;
47- const localIdentName = options . localIdentName || '[hash:base64]' ;
48- const customGetLocalIdent = options . getLocalIdent || getLocalIdent ;
49-
50- const parserOptions = {
51- url : options . url !== false ,
52- import : options . import !== false ,
53- } ;
5456
5557 const plugins = [
5658 modulesValues ,
5759 localByDefault ( {
5860 mode : options . modules ? 'local' : 'global' ,
5961 rewriteUrl ( global , url ) {
60- if ( parserOptions . url ) {
62+ if ( resolveUrl ) {
6163 // eslint-disable-next-line no-param-reassign
6264 url = url . trim ( ) ;
6365
64- if (
65- ! url . replace ( / \s / g, '' ) . length ||
66- ! loaderUtils . isUrlRequest ( url )
67- ) {
66+ if ( ! url . replace ( / \s / g, '' ) . length || ! isUrlRequest ( url ) ) {
6867 return url ;
6968 }
69+
7070 if ( global ) {
71- return loaderUtils . urlToRequest ( url ) ;
71+ return urlToRequest ( url ) ;
7272 }
7373 }
74+
7475 return url ;
7576 } ,
7677 } ) ,
7778 extractImports ( ) ,
7879 modulesScope ( {
7980 generateScopedName : function generateScopedName ( exportName ) {
81+ const localIdentName = options . localIdentName || '[hash:base64]' ;
82+ const customGetLocalIdent = options . getLocalIdent || getLocalIdent ;
83+
8084 return customGetLocalIdent ( loaderContext , localIdentName , exportName , {
8185 regExp : options . localIdentRegExp ,
8286 hashPrefix : options . hashPrefix || '' ,
@@ -86,11 +90,11 @@ module.exports = function loader(content, map) {
8690 } ) ,
8791 ] ;
8892
89- if ( options . import !== false ) {
93+ if ( resolveImport ) {
9094 plugins . push ( importParser ( parserOptions ) ) ;
9195 }
9296
93- if ( options . url !== false ) {
97+ if ( resolveUrl ) {
9498 plugins . push ( urlParser ( parserOptions ) ) ;
9599 }
96100
@@ -99,12 +103,10 @@ module.exports = function loader(content, map) {
99103 postcss ( plugins )
100104 . process ( content , {
101105 // we need a prefix to avoid path rewriting of PostCSS
102- from : `/css-loader!${ loaderUtils
103- . getRemainingRequest ( this )
106+ from : `/css-loader!${ getRemainingRequest ( this )
104107 . split ( '!' )
105108 . pop ( ) } `,
106- to : loaderUtils
107- . getCurrentRequest ( this )
109+ to : getCurrentRequest ( this )
108110 . split ( '!' )
109111 . pop ( ) ,
110112 map : options . sourceMap
@@ -121,78 +123,83 @@ module.exports = function loader(content, map) {
121123 . warnings ( )
122124 . forEach ( ( warning ) => this . emitWarning ( new Warning ( warning ) ) ) ;
123125
124- // for importing CSS
125- const importUrlPrefix = getImportPrefix ( this , options ) ;
126+ const { camelCase, exportOnlyLocals, importLoaders } = options ;
127+ const { importItems, urlItems, exports } = parserOptions ;
128+ // Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
129+ const importUrlPrefix = getImportPrefix ( this , importLoaders ) ;
130+ // Prepare replacer to change from `___CSS_LOADER_IMPORT___INDEX___` to `require('./file.css').locals`
131+ const importItemReplacer = ( item ) => {
132+ const match = placholderRegExps . importItem . exec ( item ) ;
133+ const idx = + match [ 1 ] ;
134+ const importItem = importItems [ idx ] ;
135+ const importUrl = importUrlPrefix + importItem . url ;
136+
137+ if ( exportOnlyLocals ) {
138+ return `" + require(${ stringifyRequest (
139+ this ,
140+ importUrl
141+ ) } )[${ JSON . stringify ( importItem . export ) } ] + "`;
142+ }
126143
127- let exportJs = compileExports (
128- parserOptions . exports ,
129- placeholderImportItemReplacer (
144+ return `" + require(${ stringifyRequest (
130145 this ,
131- parserOptions . importItems ,
132- importUrlPrefix ,
133- options . exportOnlyLocals
134- ) ,
135- options . camelCase
136- ) ;
146+ importUrl
147+ ) } ).locals[${ JSON . stringify ( importItem . export ) } ] + "`;
148+ } ;
137149
138- if ( options . exportOnlyLocals ) {
139- if ( exportJs ) {
140- exportJs = `module.exports = ${ exportJs } ;` ;
141- }
150+ let exportCode = compileExports ( exports , camelCase , ( valueAsString ) =>
151+ valueAsString . replace ( placholderRegExps . importItemG , importItemReplacer )
152+ ) ;
142153
143- return callback ( null , exportJs ) ;
154+ if ( exportOnlyLocals ) {
155+ return callback (
156+ null ,
157+ exportCode ? `module.exports = ${ exportCode } ;` : exportCode
158+ ) ;
144159 }
145160
146- let cssAsString = JSON . stringify ( result . css ) ;
147-
148161 const alreadyImported = { } ;
149- const importJs = parserOptions . importItems
162+ const importCode = importItems
150163 . filter ( ( imp ) => {
151164 if ( ! imp . media ) {
152165 if ( alreadyImported [ imp . url ] ) {
153166 return false ;
154167 }
168+
155169 alreadyImported [ imp . url ] = true ;
156170 }
171+
157172 return true ;
158173 } )
159174 . map ( ( imp ) => {
160175 const { url } = imp ;
161176 const media = imp . media || '' ;
162177
163- if ( ! loaderUtils . isUrlRequest ( url ) ) {
178+ if ( ! isUrlRequest ( url ) ) {
164179 return `exports.push([module.id, ${ JSON . stringify (
165180 `@import url(${ url } );`
166181 ) } , ${ JSON . stringify ( media ) } ]);`;
167182 }
168183
169184 const importUrl = importUrlPrefix + url ;
170185
171- return `exports.i(require(${ loaderUtils . stringifyRequest (
186+ return `exports.i(require(${ stringifyRequest (
172187 this ,
173188 importUrl
174189 ) } ), ${ JSON . stringify ( media ) } );`;
175190 } , this )
176191 . join ( '\n' ) ;
177192
178- cssAsString = cssAsString . replace (
193+ let cssAsString = JSON . stringify ( result . css ) . replace (
179194 placholderRegExps . importItemG ,
180- placeholderImportItemReplacer (
181- this ,
182- parserOptions . importItems ,
183- importUrlPrefix
184- )
195+ importItemReplacer
185196 ) ;
186197
187198 // helper for ensuring valid CSS strings from requires
188- let urlEscapeHelper = '' ;
189-
190- if (
191- options . url !== false &&
192- parserOptions . urlItems &&
193- parserOptions . urlItems . length > 0
194- ) {
195- urlEscapeHelper = `var escape = require(${ loaderUtils . stringifyRequest (
199+ let urlEscapeHelperCode = '' ;
200+
201+ if ( resolveUrl && urlItems && urlItems . length > 0 ) {
202+ urlEscapeHelperCode = `var escape = require(${ stringifyRequest (
196203 this ,
197204 require . resolve ( './runtime/escape.js' )
198205 ) } );\n`;
@@ -202,7 +209,7 @@ module.exports = function loader(content, map) {
202209 ( item ) => {
203210 const match = placholderRegExps . urlItem . exec ( item ) ;
204211 let idx = + match [ 1 ] ;
205- const urlItem = parserOptions . urlItems [ idx ] ;
212+ const urlItem = urlItems [ idx ] ;
206213 const { url } = urlItem ;
207214
208215 idx = url . indexOf ( '?#' ) ;
@@ -217,66 +224,66 @@ module.exports = function loader(content, map) {
217224 // idx === 0 is catched by isUrlRequest
218225 // in cases like url('webfont.eot?#iefix')
219226 urlRequest = url . substr ( 0 , idx ) ;
220- return `" + escape(require(${ loaderUtils . stringifyRequest (
227+ return `" + escape(require(${ stringifyRequest (
221228 this ,
222229 urlRequest
223230 ) } ) + "${ url . substr ( idx ) } ") + "`;
224231 }
225232
226233 urlRequest = url ;
227234
228- return `" + escape(require(${ loaderUtils . stringifyRequest (
235+ return `" + escape(require(${ stringifyRequest (
229236 this ,
230237 urlRequest
231238 ) } )) + "`;
232239 }
233240 ) ;
234241 }
235242
236- if ( exportJs ) {
237- exportJs = `exports.locals = ${ exportJs } ;` ;
243+ if ( exportCode ) {
244+ exportCode = `exports.locals = ${ exportCode } ;` ;
238245 }
239246
240- let moduleJs ;
241- if ( sourceMap && result . map ) {
242- /* eslint-disable no-param-reassign */
247+ let newMap = result . map ;
248+
249+ if ( sourceMap && newMap ) {
243250 // Add a SourceMap
244- map = result . map . toJSON ( ) ;
251+ newMap = newMap . toJSON ( ) ;
245252
246- if ( map . sources ) {
247- map . sources = map . sources . map (
253+ if ( newMap . sources ) {
254+ newMap . sources = newMap . sources . map (
248255 ( source ) =>
249256 source
250257 . split ( '!' )
251258 . pop ( )
252259 . replace ( / \\ / g, '/' ) ,
253260 this
254261 ) ;
255- map . sourceRoot = '' ;
262+ newMap . sourceRoot = '' ;
256263 }
257264
258- map . file = map . file
265+ newMap . file = newMap . file
259266 . split ( '!' )
260267 . pop ( )
261268 . replace ( / \\ / g, '/' ) ;
262- map = JSON . stringify ( map ) ;
263- /* eslint-enable no-param-reassign */
264-
265- moduleJs = `exports.push([module.id, ${ cssAsString } , "", ${ map } ]);` ;
266- } else {
267- moduleJs = `exports.push([module.id, ${ cssAsString } , ""]);` ;
269+ newMap = JSON . stringify ( newMap ) ;
268270 }
269271
272+ const runtimeCode = `exports = module.exports = require(${ stringifyRequest (
273+ this ,
274+ require . resolve ( './runtime/api' )
275+ ) } )(${ ! ! sourceMap } );`;
276+ const moduleCode = `exports.push([module.id, ${ cssAsString } , ""${
277+ newMap ? `,${ newMap } ` : ''
278+ } ]);`;
279+
270280 // Embed runtime
271281 return callback (
272282 null ,
273- `${ urlEscapeHelper } exports = module.exports = require(${ loaderUtils . stringifyRequest (
274- this ,
275- require . resolve ( './runtime/api.js' )
276- ) } )(${ sourceMap } );\n` +
277- `// imports\n${ importJs } \n\n` +
278- `// module\n${ moduleJs } \n\n` +
279- `// exports\n${ exportJs } `
283+ `${ urlEscapeHelperCode } ${ runtimeCode } \n` +
284+ `// imports\n${ importCode } \n\n` +
285+ `// module\n${ moduleCode } \n\n` +
286+ `// exports\n${ exportCode } `
280287 ) ;
281288 } )
282289 . catch ( ( error ) => {
0 commit comments