Skip to content

Commit c6e565d

Browse files
refactor: merge code from processCss with loader code
1 parent b130031 commit c6e565d

File tree

3 files changed

+254
-188
lines changed

3 files changed

+254
-188
lines changed

.eslintrc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ rules:
3838
- error
3939
- never
4040
consistent-return: 'off'
41-
consistent-this: error
41+
consistent-this: 'off'
4242
curly: 'off'
4343
default-case: 'off'
4444
dot-location: 'off'

lib/loader.js

Lines changed: 253 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -3,136 +3,261 @@
33
Author Tobias Koppers @sokra
44
*/
55
var loaderUtils = require("loader-utils");
6-
var processCss = require("./processCss");
6+
var postcss = require("postcss");
7+
var plugin = require("./plugin");
78
var getImportPrefix = require("./getImportPrefix");
89
var compileExports = require("./compile-exports");
9-
10+
var CssLoaderError = require("./CssLoaderError");
1011

1112
module.exports = function(content, map) {
12-
var callback = this.async();
13-
var query = loaderUtils.getOptions(this) || {};
14-
var camelCaseKeys = query.camelCase;
15-
var sourceMap = query.sourceMap || false;
16-
17-
if(sourceMap) {
18-
if (map) {
19-
if (typeof map === "string") {
20-
map = JSON.stringify(map);
21-
}
22-
23-
if (map.sources) {
24-
map.sources = map.sources.map(function (source) {
25-
return source.replace(/\\/g, '/');
26-
});
27-
map.sourceRoot = '';
28-
}
29-
}
30-
} else {
31-
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
32-
map = null;
33-
}
34-
35-
processCss(content, map, {
36-
from: loaderUtils.getRemainingRequest(this).split("!").pop(),
37-
to: loaderUtils.getCurrentRequest(this).split("!").pop(),
38-
query: query,
39-
loaderContext: this,
40-
sourceMap: sourceMap
41-
}, function(err, result) {
42-
if(err) return callback(err);
43-
44-
var cssAsString = JSON.stringify(result.source);
45-
46-
// for importing CSS
47-
var importUrlPrefix = getImportPrefix(this, query);
48-
49-
var alreadyImported = {};
50-
var importJs = result.importItems.filter(function(imp) {
51-
if(!imp.mediaQuery) {
52-
if(alreadyImported[imp.url])
53-
return false;
54-
alreadyImported[imp.url] = true;
55-
}
56-
return true;
57-
}).map(function(imp) {
58-
if(!loaderUtils.isUrlRequest(imp.url)) {
59-
return "exports.push([module.id, " +
60-
JSON.stringify("@import url(" + imp.url + ");") + ", " +
61-
JSON.stringify(imp.mediaQuery) + "]);";
62-
} else {
63-
var importUrl = importUrlPrefix + imp.url;
64-
return "exports.i(require(" + loaderUtils.stringifyRequest(this, importUrl) + "), " + JSON.stringify(imp.mediaQuery) + ");";
65-
}
66-
}, this).join("\n");
67-
68-
function importItemMatcher(item) {
69-
var match = result.importItemRegExp.exec(item);
70-
var idx = +match[1];
71-
var importItem = result.importItems[idx];
72-
var importUrl = importUrlPrefix + importItem.url;
73-
return "\" + require(" + loaderUtils.stringifyRequest(this, importUrl) + ").locals" +
74-
"[" + JSON.stringify(importItem.export) + "] + \"";
75-
}
76-
77-
cssAsString = cssAsString.replace(result.importItemRegExpG, importItemMatcher.bind(this));
78-
79-
// helper for ensuring valid CSS strings from requires
80-
var urlEscapeHelper = "";
81-
82-
if(query.url !== false && result.urlItems.length > 0) {
83-
urlEscapeHelper = "var escape = require(" + loaderUtils.stringifyRequest(this, require.resolve("./url/escape.js")) + ");\n";
84-
85-
cssAsString = cssAsString.replace(result.urlItemRegExpG, function(item) {
86-
var match = result.urlItemRegExp.exec(item);
87-
var idx = +match[1];
88-
var urlItem = result.urlItems[idx];
89-
var url = urlItem.url;
90-
idx = url.indexOf("?#");
91-
if(idx < 0) idx = url.indexOf("#");
92-
var urlRequest;
93-
if(idx > 0) { // idx === 0 is catched by isUrlRequest
94-
// in cases like url('webfont.eot?#iefix')
95-
urlRequest = url.substr(0, idx);
96-
return "\" + escape(require(" + loaderUtils.stringifyRequest(this, urlRequest) + ")) + \"" +
97-
url.substr(idx);
98-
}
99-
urlRequest = url;
100-
return "\" + escape(require(" + loaderUtils.stringifyRequest(this, urlRequest) + ")) + \"";
101-
}.bind(this));
102-
}
103-
104-
var exportJs = compileExports(result, importItemMatcher.bind(this), camelCaseKeys);
105-
if (exportJs) {
106-
exportJs = "exports.locals = " + exportJs + ";";
107-
}
108-
109-
var moduleJs;
110-
if(sourceMap && result.map) {
111-
// add a SourceMap
112-
map = result.map;
113-
if(map.sources) {
114-
map.sources = map.sources.map(function(source) {
115-
return source.split("!").pop().replace(/\\/g, '/');
116-
}, this);
117-
map.sourceRoot = "";
118-
}
119-
map.file = map.file.split("!").pop().replace(/\\/g, '/');
120-
map = JSON.stringify(map);
121-
moduleJs = "exports.push([module.id, " + cssAsString + ", \"\", " + map + "]);";
122-
} else {
123-
moduleJs = "exports.push([module.id, " + cssAsString + ", \"\"]);";
124-
}
125-
126-
// embed runtime
127-
callback(null, urlEscapeHelper +
128-
"exports = module.exports = require(" +
129-
loaderUtils.stringifyRequest(this, require.resolve("./runtime.js")) +
130-
")(" + sourceMap + ");\n" +
131-
"// imports\n" +
132-
importJs + "\n\n" +
133-
"// module\n" +
134-
moduleJs + "\n\n" +
135-
"// exports\n" +
136-
exportJs);
137-
}.bind(this));
13+
var callback = this.async();
14+
var query = loaderUtils.getOptions(this) || {};
15+
var camelCaseKeys = query.camelCase;
16+
var sourceMap = query.sourceMap || false;
17+
var loaderContext = this;
18+
19+
if (sourceMap) {
20+
if (map) {
21+
if (typeof map === "string") {
22+
map = JSON.stringify(map);
23+
}
24+
25+
if (map.sources) {
26+
map.sources = map.sources.map(function(source) {
27+
return source.replace(/\\/g, "/");
28+
});
29+
map.sourceRoot = "";
30+
}
31+
}
32+
} else {
33+
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
34+
map = null;
35+
}
36+
37+
var parserOptions = {
38+
url: query.url !== false,
39+
import: query.import !== false,
40+
resolve: loaderContext.resolve
41+
};
42+
43+
postcss([plugin(parserOptions)])
44+
.process(content, {
45+
// we need a prefix to avoid path rewriting of PostCSS
46+
from:
47+
"/css-loader!" +
48+
loaderUtils
49+
.getRemainingRequest(loaderContext)
50+
.split("!")
51+
.pop(),
52+
to: loaderUtils
53+
.getCurrentRequest(loaderContext)
54+
.split("!")
55+
.pop(),
56+
map: sourceMap
57+
? {
58+
prev: map,
59+
sourcesContent: true,
60+
inline: false,
61+
annotation: false
62+
}
63+
: null
64+
})
65+
.then(function(result) {
66+
const preparedResult = {
67+
source: result.css,
68+
map: result.map && result.map.toJSON(),
69+
exports: parserOptions.exports,
70+
importItems: parserOptions.importItems,
71+
importItemRegExpG: /___CSS_LOADER_IMPORT___([0-9]+)___/g,
72+
importItemRegExp: /___CSS_LOADER_IMPORT___([0-9]+)___/,
73+
urlItems: parserOptions.urlItems,
74+
urlItemRegExpG: /___CSS_LOADER_URL___([0-9]+)___/g,
75+
urlItemRegExp: /___CSS_LOADER_URL___([0-9]+)___/
76+
};
77+
78+
var cssAsString = JSON.stringify(preparedResult.source);
79+
// for importing CSS
80+
var importUrlPrefix = getImportPrefix(loaderContext, query);
81+
var alreadyImported = {};
82+
var importJs = preparedResult.importItems
83+
.filter(function(imp) {
84+
if (!imp.mediaQuery) {
85+
if (alreadyImported[imp.url]) {
86+
return false;
87+
}
88+
89+
alreadyImported[imp.url] = true;
90+
}
91+
92+
return true;
93+
})
94+
.map(function(imp) {
95+
if (!loaderUtils.isUrlRequest(imp.url)) {
96+
return (
97+
"exports.push([module.id, " +
98+
JSON.stringify("@import url(" + imp.url + ");") +
99+
", " +
100+
JSON.stringify(imp.mediaQuery) +
101+
"]);"
102+
);
103+
} else {
104+
var importUrl = importUrlPrefix + imp.url;
105+
106+
return (
107+
"exports.i(require(" +
108+
loaderUtils.stringifyRequest(this, importUrl) +
109+
"), " +
110+
JSON.stringify(imp.mediaQuery) +
111+
");"
112+
);
113+
}
114+
}, loaderContext)
115+
.join("\n");
116+
117+
function importItemMatcher(item) {
118+
var match = preparedResult.importItemRegExp.exec(item);
119+
var idx = +match[1];
120+
var importItem = preparedResult.importItems[idx];
121+
var importUrl = importUrlPrefix + importItem.url;
122+
123+
return (
124+
'" + require(' +
125+
loaderUtils.stringifyRequest(this, importUrl) +
126+
").locals" +
127+
"[" +
128+
JSON.stringify(importItem.export) +
129+
'] + "'
130+
);
131+
}
132+
133+
cssAsString = cssAsString.replace(
134+
preparedResult.importItemRegExpG,
135+
importItemMatcher.bind(loaderContext)
136+
);
137+
138+
// helper for ensuring valid CSS strings from requires
139+
var urlEscapeHelper = "";
140+
141+
if (query.url !== false && preparedResult.urlItems.length > 0) {
142+
urlEscapeHelper =
143+
"var escape = require(" +
144+
loaderUtils.stringifyRequest(
145+
loaderContext,
146+
require.resolve("./url/escape.js")
147+
) +
148+
");\n";
149+
150+
cssAsString = cssAsString.replace(
151+
preparedResult.urlItemRegExpG,
152+
function(item) {
153+
var match = preparedResult.urlItemRegExp.exec(item);
154+
var idx = +match[1];
155+
var urlItem = preparedResult.urlItems[idx];
156+
var url = urlItem.url;
157+
158+
idx = url.indexOf("?#");
159+
160+
if (idx < 0) idx = url.indexOf("#");
161+
162+
var urlRequest;
163+
164+
if (idx > 0) {
165+
// idx === 0 is catched by isUrlRequest
166+
// in cases like url('webfont.eot?#iefix')
167+
urlRequest = url.substr(0, idx);
168+
169+
return (
170+
'" + escape(require(' +
171+
loaderUtils.stringifyRequest(loaderContext, urlRequest) +
172+
')) + "' +
173+
url.substr(idx)
174+
);
175+
}
176+
177+
urlRequest = url;
178+
179+
return (
180+
'" + escape(require(' +
181+
loaderUtils.stringifyRequest(loaderContext, urlRequest) +
182+
')) + "'
183+
);
184+
}.bind(loaderContext)
185+
);
186+
}
187+
188+
var exportJs = compileExports(
189+
preparedResult,
190+
importItemMatcher.bind(loaderContext),
191+
camelCaseKeys
192+
);
193+
194+
if (exportJs) {
195+
exportJs = "exports.locals = " + exportJs + ";";
196+
}
197+
198+
var moduleJs;
199+
200+
if (sourceMap && preparedResult.map) {
201+
// add a SourceMap
202+
map = preparedResult.map;
203+
204+
if (map.sources) {
205+
map.sources = map.sources.map(function(source) {
206+
return source
207+
.split("!")
208+
.pop()
209+
.replace(/\\/g, "/");
210+
}, loaderContext);
211+
map.sourceRoot = "";
212+
}
213+
214+
map.file = map.file
215+
.split("!")
216+
.pop()
217+
.replace(/\\/g, "/");
218+
map = JSON.stringify(map);
219+
220+
moduleJs =
221+
"exports.push([module.id, " + cssAsString + ', "", ' + map + "]);";
222+
} else {
223+
moduleJs = "exports.push([module.id, " + cssAsString + ', ""]);';
224+
}
225+
226+
// embed runtime
227+
callback(
228+
null,
229+
urlEscapeHelper +
230+
"exports = module.exports = require(" +
231+
loaderUtils.stringifyRequest(
232+
loaderContext,
233+
require.resolve("./runtime.js")
234+
) +
235+
")(" +
236+
sourceMap +
237+
");\n" +
238+
"// imports\n" +
239+
importJs +
240+
"\n\n" +
241+
"// module\n" +
242+
moduleJs +
243+
"\n\n" +
244+
"// exports\n" +
245+
exportJs
246+
);
247+
})
248+
.catch(function(error) {
249+
const preparedError =
250+
error.name === "CssSyntaxError"
251+
? new CssLoaderError(
252+
"Syntax Error",
253+
error.reason,
254+
error.line != null && error.column != null
255+
? { line: error.line, column: error.column }
256+
: null,
257+
error.input.source
258+
)
259+
: error;
260+
261+
callback(preparedError);
262+
});
138263
};

0 commit comments

Comments
 (0)