Skip to content

Commit ee409c5

Browse files
refactor: loader
* refactor: placeholder regex * refactor: utils * refactor: loader
1 parent e9327c0 commit ee409c5

File tree

3 files changed

+161
-173
lines changed

3 files changed

+161
-173
lines changed

lib/loader.js

+139-45
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22
MIT License http://www.opensource.org/licenses/mit-license.php
33
Author Tobias Koppers @sokra
44
*/
5+
const postcss = require('postcss');
6+
const localByDefault = require('postcss-modules-local-by-default');
7+
const extractImports = require('postcss-modules-extract-imports');
8+
const modulesScope = require('postcss-modules-scope');
9+
const modulesValues = require('postcss-modules-values');
510
const loaderUtils = require('loader-utils');
611

7-
const processCss = require('./processCss');
12+
const { importParser, icssParser, urlParser } = require('./plugins');
813
const {
14+
getLocalIdent,
915
getImportPrefix,
1016
placeholderImportItemReplacer,
1117
compileExports,
18+
placholderRegExps,
1219
} = require('./utils');
20+
const Warning = require('./Warning');
21+
const CssSyntaxError = require('./CssSyntaxError');
1322

1423
module.exports = function loader(content, map) {
1524
const callback = this.async();
@@ -34,27 +43,92 @@ module.exports = function loader(content, map) {
3443
}
3544
/* eslint-enable no-param-reassign */
3645

37-
processCss(
38-
content,
39-
map,
40-
{
41-
loaderContext: this,
42-
loaderOptions: options,
43-
sourceMap,
44-
},
45-
(err, result) => {
46-
if (err) {
47-
return callback(err);
48-
}
46+
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+
};
54+
55+
const plugins = [
56+
modulesValues,
57+
localByDefault({
58+
mode: options.modules ? 'local' : 'global',
59+
rewriteUrl(global, url) {
60+
if (parserOptions.url) {
61+
// eslint-disable-next-line no-param-reassign
62+
url = url.trim();
63+
64+
if (
65+
!url.replace(/\s/g, '').length ||
66+
!loaderUtils.isUrlRequest(url)
67+
) {
68+
return url;
69+
}
70+
if (global) {
71+
return loaderUtils.urlToRequest(url);
72+
}
73+
}
74+
return url;
75+
},
76+
}),
77+
extractImports(),
78+
modulesScope({
79+
generateScopedName: function generateScopedName(exportName) {
80+
return customGetLocalIdent(loaderContext, localIdentName, exportName, {
81+
regExp: options.localIdentRegExp,
82+
hashPrefix: options.hashPrefix || '',
83+
context: options.context,
84+
});
85+
},
86+
}),
87+
];
88+
89+
if (options.import !== false) {
90+
plugins.push(importParser(parserOptions));
91+
}
92+
93+
if (options.url !== false) {
94+
plugins.push(urlParser(parserOptions));
95+
}
96+
97+
plugins.push(icssParser(parserOptions));
98+
99+
postcss(plugins)
100+
.process(content, {
101+
// we need a prefix to avoid path rewriting of PostCSS
102+
from: `/css-loader!${loaderUtils
103+
.getRemainingRequest(this)
104+
.split('!')
105+
.pop()}`,
106+
to: loaderUtils
107+
.getCurrentRequest(this)
108+
.split('!')
109+
.pop(),
110+
map: options.sourceMap
111+
? {
112+
prev: map,
113+
sourcesContent: true,
114+
inline: false,
115+
annotation: false,
116+
}
117+
: null,
118+
})
119+
.then((result) => {
120+
result
121+
.warnings()
122+
.forEach((warning) => this.emitWarning(new Warning(warning)));
49123

50124
// for importing CSS
51125
const importUrlPrefix = getImportPrefix(this, options);
52126

53127
let exportJs = compileExports(
54-
result,
128+
parserOptions.exports,
55129
placeholderImportItemReplacer(
56130
this,
57-
result,
131+
parserOptions.importItems,
58132
importUrlPrefix,
59133
options.exportOnlyLocals
60134
),
@@ -69,10 +143,10 @@ module.exports = function loader(content, map) {
69143
return callback(null, exportJs);
70144
}
71145

72-
let cssAsString = JSON.stringify(result.source);
146+
let cssAsString = JSON.stringify(result.css);
73147

74148
const alreadyImported = {};
75-
const importJs = result.importItems
149+
const importJs = parserOptions.importItems
76150
.filter((imp) => {
77151
if (!imp.media) {
78152
if (alreadyImported[imp.url]) {
@@ -102,48 +176,61 @@ module.exports = function loader(content, map) {
102176
.join('\n');
103177

104178
cssAsString = cssAsString.replace(
105-
result.importItemRegExpG,
106-
placeholderImportItemReplacer(this, result, importUrlPrefix)
179+
placholderRegExps.importItemG,
180+
placeholderImportItemReplacer(
181+
this,
182+
parserOptions.importItems,
183+
importUrlPrefix
184+
)
107185
);
108186

109187
// helper for ensuring valid CSS strings from requires
110188
let urlEscapeHelper = '';
111189

112190
if (
113191
options.url !== false &&
114-
result.urlItems &&
115-
result.urlItems.length > 0
192+
parserOptions.urlItems &&
193+
parserOptions.urlItems.length > 0
116194
) {
117195
urlEscapeHelper = `var escape = require(${loaderUtils.stringifyRequest(
118196
this,
119197
require.resolve('./runtime/escape.js')
120198
)});\n`;
121199

122-
cssAsString = cssAsString.replace(result.urlItemRegExpG, (item) => {
123-
const match = result.urlItemRegExp.exec(item);
124-
let idx = +match[1];
125-
const urlItem = result.urlItems[idx];
126-
const { url } = urlItem;
127-
idx = url.indexOf('?#');
128-
if (idx < 0) {
129-
idx = url.indexOf('#');
130-
}
131-
let urlRequest;
132-
if (idx > 0) {
133-
// idx === 0 is catched by isUrlRequest
134-
// in cases like url('webfont.eot?#iefix')
135-
urlRequest = url.substr(0, idx);
200+
cssAsString = cssAsString.replace(
201+
placholderRegExps.urlItemG,
202+
(item) => {
203+
const match = placholderRegExps.urlItem.exec(item);
204+
let idx = +match[1];
205+
const urlItem = parserOptions.urlItems[idx];
206+
const { url } = urlItem;
207+
208+
idx = url.indexOf('?#');
209+
210+
if (idx < 0) {
211+
idx = url.indexOf('#');
212+
}
213+
214+
let urlRequest;
215+
216+
if (idx > 0) {
217+
// idx === 0 is catched by isUrlRequest
218+
// in cases like url('webfont.eot?#iefix')
219+
urlRequest = url.substr(0, idx);
220+
return `" + escape(require(${loaderUtils.stringifyRequest(
221+
this,
222+
urlRequest
223+
)}) + "${url.substr(idx)}") + "`;
224+
}
225+
226+
urlRequest = url;
227+
136228
return `" + escape(require(${loaderUtils.stringifyRequest(
137229
this,
138230
urlRequest
139-
)}) + "${url.substr(idx)}") + "`;
231+
)})) + "`;
140232
}
141-
urlRequest = url;
142-
return `" + escape(require(${loaderUtils.stringifyRequest(
143-
this,
144-
urlRequest
145-
)})) + "`;
146-
});
233+
);
147234
}
148235

149236
if (exportJs) {
@@ -154,7 +241,8 @@ module.exports = function loader(content, map) {
154241
if (sourceMap && result.map) {
155242
/* eslint-disable no-param-reassign */
156243
// Add a SourceMap
157-
({ map } = result);
244+
map = result.map.toJSON();
245+
158246
if (map.sources) {
159247
map.sources = map.sources.map(
160248
(source) =>
@@ -166,12 +254,14 @@ module.exports = function loader(content, map) {
166254
);
167255
map.sourceRoot = '';
168256
}
257+
169258
map.file = map.file
170259
.split('!')
171260
.pop()
172261
.replace(/\\/g, '/');
173262
map = JSON.stringify(map);
174263
/* eslint-enable no-param-reassign */
264+
175265
moduleJs = `exports.push([module.id, ${cssAsString}, "", ${map}]);`;
176266
} else {
177267
moduleJs = `exports.push([module.id, ${cssAsString}, ""]);`;
@@ -188,6 +278,10 @@ module.exports = function loader(content, map) {
188278
`// module\n${moduleJs}\n\n` +
189279
`// exports\n${exportJs}`
190280
);
191-
}
192-
);
281+
})
282+
.catch((error) => {
283+
callback(
284+
error.name === 'CssSyntaxError' ? new CssSyntaxError(error) : error
285+
);
286+
});
193287
};

lib/processCss.js

-120
This file was deleted.

0 commit comments

Comments
 (0)