55import validateOptions from 'schema-utils' ;
66import postcss from 'postcss' ;
77import postcssPkg from 'postcss/package.json' ;
8- import localByDefault from 'postcss-modules-local-by-default' ;
9- import extractImports from 'postcss-modules-extract-imports' ;
10- import modulesScope from 'postcss-modules-scope' ;
11- import modulesValues from 'postcss-modules-values' ;
8+
129import {
1310 getOptions ,
1411 isUrlRequest ,
15- urlToRequest ,
1612 getRemainingRequest ,
1713 getCurrentRequest ,
1814 stringifyRequest ,
1915} from 'loader-utils' ;
20- import normalizePath from 'normalize-path' ;
2116
2217import schema from './options.json' ;
2318import { importParser , icssParser , urlParser } from './plugins' ;
2419import {
25- getLocalIdent ,
26- getImportPrefix ,
20+ normalizeSourceMap ,
21+ getModulesPlugins ,
2722 placholderRegExps ,
28- camelCase ,
29- dashesCamelCase ,
23+ getImportPrefix ,
24+ getImportItemReplacer ,
3025 getFilter ,
26+ getExports ,
27+ getImports ,
3128} from './utils' ;
3229import Warning from './Warning' ;
3330import CssSyntaxError from './CssSyntaxError' ;
@@ -40,35 +37,14 @@ export default function loader(content, map, meta) {
4037 const callback = this . async ( ) ;
4138 const sourceMap = options . sourceMap || false ;
4239
43- /* eslint-disable no-param-reassign */
44- if ( sourceMap ) {
45- if ( map ) {
46- // Some loader emit source map as string
47- // Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON.
48- if ( typeof map === 'string' ) {
49- map = JSON . parse ( map . replace ( / ^ \) ] } ' [ ^ \n ] * \n / , '' ) ) ;
50- }
51-
52- // Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
53- // We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
54-
55- if ( map . file ) {
56- map . file = normalizePath ( map . file ) ;
57- }
58-
59- if ( map . sourceRoot ) {
60- map . sourceRoot = normalizePath ( map . sourceRoot ) ;
61- }
62-
63- if ( map . sources ) {
64- map . sources = map . sources . map ( ( source ) => normalizePath ( source ) ) ;
65- }
66- }
40+ if ( sourceMap && map ) {
41+ // eslint-disable-next-line no-param-reassign
42+ map = normalizeSourceMap ( map ) ;
6743 } else {
6844 // Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
45+ // eslint-disable-next-line no-param-reassign
6946 map = null ;
7047 }
71- /* eslint-enable no-param-reassign */
7248
7349 // Reuse CSS AST (PostCSS AST e.g 'postcss-loader') to avoid reparsing
7450 if ( meta ) {
@@ -83,32 +59,7 @@ export default function loader(content, map, meta) {
8359 const plugins = [ ] ;
8460
8561 if ( options . modules ) {
86- const loaderContext = this ;
87- const mode =
88- typeof options . modules === 'boolean' ? 'local' : options . modules ;
89-
90- plugins . push (
91- modulesValues ,
92- localByDefault ( { mode } ) ,
93- extractImports ( ) ,
94- modulesScope ( {
95- generateScopedName : function generateScopedName ( exportName ) {
96- const localIdentName = options . localIdentName || '[hash:base64]' ;
97- const customGetLocalIdent = options . getLocalIdent || getLocalIdent ;
98-
99- return customGetLocalIdent (
100- loaderContext ,
101- localIdentName ,
102- exportName ,
103- {
104- regExp : options . localIdentRegExp ,
105- hashPrefix : options . hashPrefix || '' ,
106- context : options . context ,
107- }
108- ) ;
109- } ,
110- } )
111- ) ;
62+ plugins . push ( ...getModulesPlugins ( options , this ) ) ;
11263 }
11364
11465 if ( options . import !== false ) {
@@ -153,93 +104,22 @@ export default function loader(content, map, meta) {
153104 . forEach ( ( warning ) => this . emitWarning ( new Warning ( warning ) ) ) ;
154105
155106 const messages = result . messages || [ ] ;
107+ const { exportOnlyLocals, importLoaders, camelCase } = options ;
156108
157109 // Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
158- const importUrlPrefix = getImportPrefix ( this , options . importLoaders ) ;
110+ const importPrefix = getImportPrefix ( this , importLoaders ) ;
159111
160112 // Prepare replacer to change from `___CSS_LOADER_IMPORT___INDEX___` to `require('./file.css').locals`
161- const importItemReplacer = ( placeholder ) => {
162- const match = placholderRegExps . importItem . exec ( placeholder ) ;
163- const idx = Number ( match [ 1 ] ) ;
164-
165- const message = messages . find (
166- // eslint-disable-next-line no-shadow
167- ( message ) =>
168- message . type === 'icss-import' &&
169- message . item &&
170- message . item . index === idx
171- ) ;
172-
173- if ( ! message ) {
174- return placeholder ;
175- }
176-
177- const { item } = message ;
178- const importUrl = importUrlPrefix + urlToRequest ( item . url ) ;
179-
180- if ( options . exportOnlyLocals ) {
181- return `" + require(${ stringifyRequest (
182- this ,
183- importUrl
184- ) } )[${ JSON . stringify ( item . export ) } ] + "`;
185- }
186-
187- return `" + require(${ stringifyRequest (
188- this ,
189- importUrl
190- ) } ).locals[${ JSON . stringify ( item . export ) } ] + "`;
191- } ;
192-
193- const exports = messages
194- . filter ( ( message ) => message . type === 'export' )
195- . reduce ( ( accumulator , message ) => {
196- const { key, value } = message . item ;
197-
198- let valueAsString = JSON . stringify ( value ) ;
199-
200- valueAsString = valueAsString . replace (
201- placholderRegExps . importItemG ,
202- importItemReplacer
203- ) ;
204-
205- function addEntry ( k ) {
206- accumulator . push ( `\t${ JSON . stringify ( k ) } : ${ valueAsString } ` ) ;
207- }
208-
209- let targetKey ;
210-
211- switch ( options . camelCase ) {
212- case true :
213- addEntry ( key ) ;
214- targetKey = camelCase ( key ) ;
215-
216- if ( targetKey !== key ) {
217- addEntry ( targetKey ) ;
218- }
219- break ;
220- case 'dashes' :
221- addEntry ( key ) ;
222- targetKey = dashesCamelCase ( key ) ;
223-
224- if ( targetKey !== key ) {
225- addEntry ( targetKey ) ;
226- }
227- break ;
228- case 'only' :
229- addEntry ( camelCase ( key ) ) ;
230- break ;
231- case 'dashesOnly' :
232- addEntry ( dashesCamelCase ( key ) ) ;
233- break ;
234- default :
235- addEntry ( key ) ;
236- break ;
237- }
113+ const importItemReplacer = getImportItemReplacer (
114+ messages ,
115+ this ,
116+ importPrefix ,
117+ exportOnlyLocals
118+ ) ;
238119
239- return accumulator ;
240- } , [ ] ) ;
120+ const exports = getExports ( messages , camelCase , importItemReplacer ) ;
241121
242- if ( options . exportOnlyLocals ) {
122+ if ( exportOnlyLocals ) {
243123 return callback (
244124 null ,
245125 exports . length > 0
@@ -248,69 +128,23 @@ export default function loader(content, map, meta) {
248128 ) ;
249129 }
250130
251- const imports = messages
252- . filter ( ( message ) => message . type === 'import' )
253- . map ( ( message ) => {
254- const { url } = message . item ;
255- const media = message . item . media || '' ;
256-
257- if ( ! isUrlRequest ( url ) ) {
258- return `exports.push([module.id, ${ JSON . stringify (
259- `@import url(${ url } );`
260- ) } , ${ JSON . stringify ( media ) } ]);`;
261- }
262-
263- const importUrl = importUrlPrefix + urlToRequest ( url ) ;
264-
265- return `exports.i(require(${ stringifyRequest (
266- this ,
267- importUrl
268- ) } ), ${ JSON . stringify ( media ) } );`;
269- } , this ) ;
270-
271131 let cssAsString = JSON . stringify ( result . css ) . replace (
272132 placholderRegExps . importItemG ,
273133 importItemReplacer
274134 ) ;
275135
276- // Helper for ensuring valid CSS strings from requires
277- let hasUrlEscapeHelper = false ;
278-
279- messages
280- . filter ( ( message ) => message . type === 'url' )
281- . forEach ( ( message ) => {
282- if ( ! hasUrlEscapeHelper ) {
283- imports . push (
284- `var urlEscape = require(${ stringifyRequest (
285- this ,
286- require . resolve ( './runtime/url-escape.js' )
287- ) } );`
288- ) ;
289-
290- hasUrlEscapeHelper = true ;
291- }
292-
293- const { item } = message ;
294- const { url, placeholder, needQuotes } = item ;
295- // Remove `#hash` and `?#hash` from `require`
296- const [ normalizedUrl , singleQuery , hashValue ] = url . split ( / ( \? ) ? # / ) ;
297- const hash =
298- singleQuery || hashValue
299- ? `"${ singleQuery ? '?' : '' } ${ hashValue ? `#${ hashValue } ` : '' } "`
300- : '' ;
136+ const imports = getImports ( messages , importPrefix , this , ( message ) => {
137+ if ( message . type !== 'url' ) {
138+ return ;
139+ }
301140
302- imports . push (
303- `var ${ placeholder } = urlEscape(require(${ stringifyRequest (
304- this ,
305- urlToRequest ( normalizedUrl )
306- ) } )${ hash ? ` + ${ hash } ` : '' } ${ needQuotes ? ', true' : '' } );`
307- ) ;
141+ const { placeholder } = message . item ;
308142
309- cssAsString = cssAsString . replace (
310- new RegExp ( placeholder , 'g' ) ,
311- ( ) => `" + ${ placeholder } + "`
312- ) ;
313- } ) ;
143+ cssAsString = cssAsString . replace (
144+ new RegExp ( placeholder , 'g' ) ,
145+ ( ) => `" + ${ placeholder } + "`
146+ ) ;
147+ } ) ;
314148
315149 const runtimeCode = `exports = module.exports = require(${ stringifyRequest (
316150 this ,
0 commit comments