Skip to content

Commit c012f9b

Browse files
refactor: move postcss plugin into own file
1 parent 09e21c1 commit c012f9b

File tree

2 files changed

+194
-189
lines changed

2 files changed

+194
-189
lines changed

lib/plugin.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
var postcss = require("postcss");
2+
var valueParser = require("postcss-value-parser");
3+
var Tokenizer = require("css-selector-tokenizer");
4+
var loaderUtils = require("loader-utils");
5+
var icssUtils = require("icss-utils");
6+
7+
module.exports = postcss.plugin("css-loader-parser", function(options) {
8+
return function(css) {
9+
var imports = {};
10+
var exports = {};
11+
var importItems = [];
12+
var urlItems = [];
13+
14+
function replaceImportsInString(str) {
15+
if (options.import) {
16+
var tokens = valueParser(str);
17+
tokens.walk(function(node) {
18+
if (node.type !== "word") {
19+
return;
20+
}
21+
var token = node.value;
22+
var importIndex = imports["$" + token];
23+
if (typeof importIndex === "number") {
24+
node.value = "___CSS_LOADER_IMPORT___" + importIndex + "___";
25+
}
26+
});
27+
return tokens.toString();
28+
}
29+
return str;
30+
}
31+
32+
if (options.import) {
33+
css.walkAtRules(/^import$/i, function(rule) {
34+
var values = Tokenizer.parseValues(rule.params);
35+
var url = values.nodes[0].nodes[0];
36+
if (url && url.type === "url") {
37+
url = url.url;
38+
} else if (url && url.type === "string") {
39+
url = url.value;
40+
} else throw rule.error("Unexpected format " + rule.params);
41+
if (!url.replace(/\s/g, "").length) {
42+
return;
43+
}
44+
values.nodes[0].nodes.shift();
45+
var mediaQuery = Tokenizer.stringifyValues(values);
46+
47+
if (loaderUtils.isUrlRequest(url)) {
48+
url = loaderUtils.urlToRequest(url);
49+
}
50+
51+
importItems.push({
52+
url: url,
53+
mediaQuery: mediaQuery
54+
});
55+
rule.remove();
56+
});
57+
}
58+
59+
var icss = icssUtils.extractICSS(css);
60+
exports = icss.icssExports;
61+
Object.keys(icss.icssImports).forEach(function(key) {
62+
var url = loaderUtils.parseString(key);
63+
Object.keys(icss.icssImports[key]).forEach(function(prop) {
64+
imports["$" + prop] = importItems.length;
65+
importItems.push({
66+
url: url,
67+
export: icss.icssImports[key][prop]
68+
});
69+
});
70+
});
71+
72+
Object.keys(exports).forEach(function(exportName) {
73+
exports[exportName] = replaceImportsInString(exports[exportName]);
74+
});
75+
76+
function processNode(item) {
77+
switch (item.type) {
78+
case "value":
79+
item.nodes.forEach(processNode);
80+
break;
81+
case "nested-item":
82+
item.nodes.forEach(processNode);
83+
break;
84+
case "item":
85+
var importIndex = imports["$" + item.name];
86+
if (typeof importIndex === "number") {
87+
item.name = "___CSS_LOADER_IMPORT___" + importIndex + "___";
88+
}
89+
break;
90+
case "url":
91+
if (
92+
options.url &&
93+
item.url.replace(/\s/g, "").length &&
94+
!/^#/.test(item.url) &&
95+
loaderUtils.isUrlRequest(item.url)
96+
) {
97+
// Strip quotes, they will be re-added if the module needs them
98+
item.stringType = "";
99+
delete item.innerSpacingBefore;
100+
delete item.innerSpacingAfter;
101+
// For backward-compat after dropping css modules
102+
var url = loaderUtils.urlToRequest(item.url.trim());
103+
item.url = "___CSS_LOADER_URL___" + urlItems.length + "___";
104+
urlItems.push({
105+
url: url
106+
});
107+
}
108+
break;
109+
}
110+
}
111+
112+
css.walkDecls(function(decl) {
113+
var values = Tokenizer.parseValues(decl.value);
114+
values.nodes.forEach(function(value) {
115+
value.nodes.forEach(processNode);
116+
});
117+
decl.value = Tokenizer.stringifyValues(values);
118+
});
119+
css.walkAtRules(function(atrule) {
120+
if (typeof atrule.params === "string") {
121+
atrule.params = replaceImportsInString(atrule.params);
122+
}
123+
});
124+
125+
options.importItems = importItems;
126+
options.urlItems = urlItems;
127+
options.exports = exports;
128+
};
129+
});

lib/processCss.js

Lines changed: 65 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -3,204 +3,80 @@
33
Author Tobias Koppers @sokra
44
*/
55
var formatCodeFrame = require("babel-code-frame");
6-
var Tokenizer = require("css-selector-tokenizer");
76
var postcss = require("postcss");
8-
var loaderUtils = require("loader-utils");
9-
10-
var icssUtils = require('icss-utils');
11-
var valueParser = require('postcss-value-parser');
12-
13-
var parserPlugin = postcss.plugin("css-loader-parser", function(options) {
14-
return function(css) {
15-
var imports = {};
16-
var exports = {};
17-
var importItems = [];
18-
var urlItems = [];
19-
20-
function replaceImportsInString(str) {
21-
if(options.import) {
22-
var tokens = valueParser(str);
23-
tokens.walk(function (node) {
24-
if (node.type !== 'word') {
25-
return;
26-
}
27-
var token = node.value;
28-
var importIndex = imports["$" + token];
29-
if(typeof importIndex === "number") {
30-
node.value = "___CSS_LOADER_IMPORT___" + importIndex + "___";
31-
}
32-
})
33-
return tokens.toString();
34-
}
35-
return str;
36-
}
37-
38-
if(options.import) {
39-
css.walkAtRules(/^import$/i, function(rule) {
40-
var values = Tokenizer.parseValues(rule.params);
41-
var url = values.nodes[0].nodes[0];
42-
if(url && url.type === "url") {
43-
url = url.url;
44-
} else if(url && url.type === "string") {
45-
url = url.value;
46-
} else throw rule.error("Unexpected format " + rule.params);
47-
if (!url.replace(/\s/g, '').length) {
48-
return;
49-
}
50-
values.nodes[0].nodes.shift();
51-
var mediaQuery = Tokenizer.stringifyValues(values);
52-
53-
if(loaderUtils.isUrlRequest(url)) {
54-
url = loaderUtils.urlToRequest(url);
55-
}
56-
57-
importItems.push({
58-
url: url,
59-
mediaQuery: mediaQuery
60-
});
61-
rule.remove();
62-
});
63-
}
64-
65-
var icss = icssUtils.extractICSS(css);
66-
exports = icss.icssExports;
67-
Object.keys(icss.icssImports).forEach(function(key) {
68-
var url = loaderUtils.parseString(key);
69-
Object.keys(icss.icssImports[key]).forEach(function(prop) {
70-
imports["$" + prop] = importItems.length;
71-
importItems.push({
72-
url: url,
73-
export: icss.icssImports[key][prop]
74-
});
75-
})
76-
});
77-
78-
Object.keys(exports).forEach(function(exportName) {
79-
exports[exportName] = replaceImportsInString(exports[exportName]);
80-
});
81-
82-
function processNode(item) {
83-
switch (item.type) {
84-
case "value":
85-
item.nodes.forEach(processNode);
86-
break;
87-
case "nested-item":
88-
item.nodes.forEach(processNode);
89-
break;
90-
case "item":
91-
var importIndex = imports["$" + item.name];
92-
if (typeof importIndex === "number") {
93-
item.name = "___CSS_LOADER_IMPORT___" + importIndex + "___";
94-
}
95-
break;
96-
case "url":
97-
if (options.url && item.url.replace(/\s/g, '').length && !/^#/.test(item.url) && loaderUtils.isUrlRequest(item.url)) {
98-
// Strip quotes, they will be re-added if the module needs them
99-
item.stringType = "";
100-
delete item.innerSpacingBefore;
101-
delete item.innerSpacingAfter;
102-
// For backward-compat after dropping css modules
103-
var url = loaderUtils.urlToRequest(item.url.trim());
104-
item.url = "___CSS_LOADER_URL___" + urlItems.length + "___";
105-
urlItems.push({
106-
url: url
107-
});
108-
}
109-
break;
110-
}
111-
}
112-
113-
css.walkDecls(function(decl) {
114-
var values = Tokenizer.parseValues(decl.value);
115-
values.nodes.forEach(function(value) {
116-
value.nodes.forEach(processNode);
117-
});
118-
decl.value = Tokenizer.stringifyValues(values);
119-
});
120-
css.walkAtRules(function(atrule) {
121-
if(typeof atrule.params === "string") {
122-
atrule.params = replaceImportsInString(atrule.params);
123-
}
124-
});
125-
126-
options.importItems = importItems;
127-
options.urlItems = urlItems;
128-
options.exports = exports;
129-
};
130-
});
7+
var plugin = require("./plugin");
1318

1329
module.exports = function processCss(inputSource, inputMap, options, callback) {
133-
var query = options.query;
134-
135-
var parserOptions = {
136-
url: query.url !== false,
137-
import: query.import !== false,
138-
resolve: options.resolve
139-
};
140-
141-
var pipeline = postcss([
142-
parserPlugin(parserOptions)
143-
]);
144-
145-
pipeline.process(inputSource, {
146-
// we need a prefix to avoid path rewriting of PostCSS
147-
from: "/css-loader!" + options.from,
148-
to: options.to,
149-
map: options.sourceMap ? {
150-
prev: inputMap,
151-
sourcesContent: true,
152-
inline: false,
153-
annotation: false
154-
} : null
155-
}).then(function(result) {
156-
callback(null, {
157-
source: result.css,
158-
map: result.map && result.map.toJSON(),
159-
exports: parserOptions.exports,
160-
importItems: parserOptions.importItems,
161-
importItemRegExpG: /___CSS_LOADER_IMPORT___([0-9]+)___/g,
162-
importItemRegExp: /___CSS_LOADER_IMPORT___([0-9]+)___/,
163-
urlItems: parserOptions.urlItems,
164-
urlItemRegExpG: /___CSS_LOADER_URL___([0-9]+)___/g,
165-
urlItemRegExp: /___CSS_LOADER_URL___([0-9]+)___/
166-
});
167-
}).catch(function(err) {
168-
if (err.name === 'CssSyntaxError') {
169-
var wrappedError = new CSSLoaderError(
170-
'Syntax Error',
171-
err.reason,
172-
err.line != null && err.column != null
173-
? {line: err.line, column: err.column}
174-
: null,
175-
err.input.source
176-
);
177-
callback(wrappedError);
178-
} else {
179-
callback(err);
180-
}
181-
});
10+
var query = options.query;
11+
var parserOptions = {
12+
url: query.url !== false,
13+
import: query.import !== false,
14+
resolve: options.resolve
15+
};
16+
17+
postcss([plugin(parserOptions)])
18+
.process(inputSource, {
19+
// we need a prefix to avoid path rewriting of PostCSS
20+
from: "/css-loader!" + options.from,
21+
to: options.to,
22+
map: options.sourceMap
23+
? {
24+
prev: inputMap,
25+
sourcesContent: true,
26+
inline: false,
27+
annotation: false
28+
}
29+
: null
30+
})
31+
.then(function(result) {
32+
callback(null, {
33+
source: result.css,
34+
map: result.map && result.map.toJSON(),
35+
exports: parserOptions.exports,
36+
importItems: parserOptions.importItems,
37+
importItemRegExpG: /___CSS_LOADER_IMPORT___([0-9]+)___/g,
38+
importItemRegExp: /___CSS_LOADER_IMPORT___([0-9]+)___/,
39+
urlItems: parserOptions.urlItems,
40+
urlItemRegExpG: /___CSS_LOADER_URL___([0-9]+)___/g,
41+
urlItemRegExp: /___CSS_LOADER_URL___([0-9]+)___/
42+
});
43+
})
44+
.catch(function(err) {
45+
if (err.name === "CssSyntaxError") {
46+
var wrappedError = new CSSLoaderError(
47+
"Syntax Error",
48+
err.reason,
49+
err.line != null && err.column != null
50+
? { line: err.line, column: err.column }
51+
: null,
52+
err.input.source
53+
);
54+
callback(wrappedError);
55+
} else {
56+
callback(err);
57+
}
58+
});
18259
};
18360

18461
function formatMessage(message, loc, source) {
185-
var formatted = message;
186-
if (loc) {
187-
formatted = formatted
188-
+ ' (' + loc.line + ':' + loc.column + ')';
189-
}
190-
if (loc && source) {
191-
formatted = formatted
192-
+ '\n\n' + formatCodeFrame(source, loc.line, loc.column) + '\n';
193-
}
194-
return formatted;
62+
var formatted = message;
63+
if (loc) {
64+
formatted = formatted + " (" + loc.line + ":" + loc.column + ")";
65+
}
66+
if (loc && source) {
67+
formatted =
68+
formatted + "\n\n" + formatCodeFrame(source, loc.line, loc.column) + "\n";
69+
}
70+
return formatted;
19571
}
19672

19773
function CSSLoaderError(name, message, loc, source, error) {
198-
Error.call(this);
199-
Error.captureStackTrace(this, CSSLoaderError);
200-
this.name = name;
201-
this.error = error;
202-
this.message = formatMessage(message, loc, source);
203-
this.hideStack = true;
74+
Error.call(this);
75+
Error.captureStackTrace(this, CSSLoaderError);
76+
this.name = name;
77+
this.error = error;
78+
this.message = formatMessage(message, loc, source);
79+
this.hideStack = true;
20480
}
20581

20682
CSSLoaderError.prototype = Object.create(Error.prototype);

0 commit comments

Comments
 (0)