5
5
import validateOptions from 'schema-utils' ;
6
6
import postcss from 'postcss' ;
7
7
import 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
+
12
9
import {
13
10
getOptions ,
14
11
isUrlRequest ,
15
- urlToRequest ,
16
12
getRemainingRequest ,
17
13
getCurrentRequest ,
18
14
stringifyRequest ,
19
15
} from 'loader-utils' ;
20
- import normalizePath from 'normalize-path' ;
21
16
22
17
import schema from './options.json' ;
23
18
import { importParser , icssParser , urlParser } from './plugins' ;
24
19
import {
25
- getLocalIdent ,
26
- getImportPrefix ,
20
+ normalizeSourceMap ,
21
+ getModulesPlugins ,
27
22
placholderRegExps ,
28
- camelCase ,
29
- dashesCamelCase ,
23
+ getImportPrefix ,
24
+ getImportItemReplacer ,
30
25
getFilter ,
26
+ getExports ,
27
+ getImports ,
31
28
} from './utils' ;
32
29
import Warning from './Warning' ;
33
30
import CssSyntaxError from './CssSyntaxError' ;
@@ -40,35 +37,14 @@ export default function loader(content, map, meta) {
40
37
const callback = this . async ( ) ;
41
38
const sourceMap = options . sourceMap || false ;
42
39
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 ) ;
67
43
} else {
68
44
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
45
+ // eslint-disable-next-line no-param-reassign
69
46
map = null ;
70
47
}
71
- /* eslint-enable no-param-reassign */
72
48
73
49
// Reuse CSS AST (PostCSS AST e.g 'postcss-loader') to avoid reparsing
74
50
if ( meta ) {
@@ -83,32 +59,7 @@ export default function loader(content, map, meta) {
83
59
const plugins = [ ] ;
84
60
85
61
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 ) ) ;
112
63
}
113
64
114
65
if ( options . import !== false ) {
@@ -153,93 +104,22 @@ export default function loader(content, map, meta) {
153
104
. forEach ( ( warning ) => this . emitWarning ( new Warning ( warning ) ) ) ;
154
105
155
106
const messages = result . messages || [ ] ;
107
+ const { exportOnlyLocals, importLoaders, camelCase } = options ;
156
108
157
109
// 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 ) ;
159
111
160
112
// 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
+ ) ;
238
119
239
- return accumulator ;
240
- } , [ ] ) ;
120
+ const exports = getExports ( messages , camelCase , importItemReplacer ) ;
241
121
242
- if ( options . exportOnlyLocals ) {
122
+ if ( exportOnlyLocals ) {
243
123
return callback (
244
124
null ,
245
125
exports . length > 0
@@ -248,69 +128,23 @@ export default function loader(content, map, meta) {
248
128
) ;
249
129
}
250
130
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
-
271
131
let cssAsString = JSON . stringify ( result . css ) . replace (
272
132
placholderRegExps . importItemG ,
273
133
importItemReplacer
274
134
) ;
275
135
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
+ }
301
140
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 ;
308
142
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
+ } ) ;
314
148
315
149
const runtimeCode = `exports = module.exports = require(${ stringifyRequest (
316
150
this ,
0 commit comments