Skip to content

Commit 6ea1d0f

Browse files
committed
use rework css instead of csso
1 parent 7b50d4f commit 6ea1d0f

File tree

3 files changed

+104
-131
lines changed

3 files changed

+104
-131
lines changed

index.js

Lines changed: 79 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,51 @@
22
MIT License http://www.opensource.org/licenses/mit-license.php
33
Author Tobias Koppers @sokra
44
*/
5-
var csso = require("csso");
6-
var SourceMapGenerator = require("source-map").SourceMapGenerator;
5+
var css = require("css");
6+
var SourceNode = require("source-map").SourceNode;
7+
var SourceMapConsumer = require("source-map").SourceMapConsumer;
78
var loaderUtils = require("loader-utils");
89

910
module.exports = function(content, map) {
1011
this.cacheable && this.cacheable();
1112
var result = [];
12-
var queryString = this.query || "";
13+
var cssRequest = loaderUtils.getRemainingRequest(this);
1314
var query = loaderUtils.parseQuery(this.query);
1415
var root = query.root;
1516
var forceMinimize = query.minimize;
1617
var importLoaders = parseInt(query.importLoaders, 10) || 0;
1718
var minimize = typeof forceMinimize !== "undefined" ? !!forceMinimize : (this && this.minimize);
18-
var tree = csso.parse(content, "stylesheet");
19-
if(tree && minimize) {
20-
tree = csso.compress(tree, query.disableStructuralMinification);
21-
tree = csso.cleanInfo(tree);
22-
}
19+
var genSourceMap = query.sourceMap;
2320

24-
if(tree) {
25-
var imports = extractImports(tree);
26-
annotateUrls(tree);
21+
var tree = css.parse(content, {
22+
source: cssRequest
23+
});
2724

28-
imports.forEach(function(imp) {
29-
if(!loaderUtils.isUrlRequest(imp.url)) {
30-
result.push("exports.push([module.id, " + JSON.stringify("@import url(" + imp.url + ");") + ", " + JSON.stringify(imp.media.join("")) + "]);");
31-
} else {
32-
var importUrl = "-!" +
33-
this.loaders.slice(
34-
this.loaderIndex,
35-
this.loaderIndex + 1 + importLoaders
36-
).map(function(x) { return x.request; }).join("!") + "!" +
37-
loaderUtils.urlToRequest(imp.url);
38-
result.push("require(" + JSON.stringify(require.resolve("./mergeImport")) + ")(exports, require(" + JSON.stringify(importUrl) + "), " + JSON.stringify(imp.media.join("")) + ");");
39-
}
40-
}, this);
41-
}
25+
var imports = extractImports(tree.stylesheet);
26+
annotateUrls(tree);
4227

43-
var css = JSON.stringify(tree ? csso.translate(tree) : "");
28+
imports.forEach(function(imp) {
29+
if(!loaderUtils.isUrlRequest(imp.url)) {
30+
result.push("exports.push([module.id, " + JSON.stringify("@import url(" + imp.url + ");") + ", " + JSON.stringify(imp.media.join("")) + "]);");
31+
} else {
32+
var importUrl = "-!" +
33+
this.loaders.slice(
34+
this.loaderIndex,
35+
this.loaderIndex + 1 + importLoaders
36+
).map(function(x) { return x.request; }).join("!") + "!" +
37+
loaderUtils.urlToRequest(imp.url);
38+
result.push("require(" + JSON.stringify(require.resolve("./mergeImport")) + ")(exports, require(" + JSON.stringify(importUrl) + "), " + JSON.stringify(imp.media.join(" ")) + ");");
39+
}
40+
}, this);
41+
42+
43+
var cssResult = css.stringify(tree, {
44+
compress: !!minimize,
45+
sourcemap: genSourceMap
46+
});
47+
var cssString = JSON.stringify(genSourceMap ? cssResult.code : cssResult);
4448
var uriRegExp = /%CSSURL\[%(.*?)%\]CSSURL%/g;
45-
css = css.replace(uriRegExp, function(str) {
49+
cssString = cssString.replace(uriRegExp, function(str) {
4650
var match = /^%CSSURL\[%(["']?(.*?)["']?)%\]CSSURL%$/.exec(JSON.parse('"' + str + '"'));
4751
var url = loaderUtils.parseString(match[2]);
4852
if(!loaderUtils.isUrlRequest(match[2], root)) return JSON.stringify(match[1]).replace(/^"|"$/g, "");
@@ -58,107 +62,72 @@ module.exports = function(content, map) {
5862
}
5963
return "\"+require(" + JSON.stringify(loaderUtils.urlToRequest(url, root)) + ")+\"";
6064
});
61-
if(query.sourceMap && !minimize) {
62-
var cssRequest = loaderUtils.getRemainingRequest(this);
65+
if(genSourceMap) {
6366
var request = loaderUtils.getCurrentRequest(this);
64-
if(!map) {
65-
var sourceMap = new SourceMapGenerator({
67+
68+
if(map) {
69+
var node = SourceNode.fromStringWithSourceMap(cssResult.code, new SourceMapConsumer(cssResult.map));
70+
node.applySourceMap(map);
71+
map = node.toStringWithSourceMap({
6672
file: request
67-
});
68-
var lines = content.split("\n").length;
69-
for(var i = 0; i < lines; i++) {
70-
sourceMap.addMapping({
71-
generated: {
72-
line: i+1,
73-
column: 0
74-
},
75-
source: cssRequest,
76-
original: {
77-
line: i+1,
78-
column: 0
79-
},
80-
});
81-
}
82-
sourceMap.setSourceContent(cssRequest, content);
83-
map = JSON.stringify(sourceMap.toJSON());
84-
} else if(typeof map !== "string") {
85-
map = JSON.stringify(map);
73+
}).map.toString();
74+
} else {
75+
map = JSON.stringify(cssResult.map);
8676
}
87-
result.push("exports.push([module.id, " + css + ", \"\", " + map + "]);");
77+
result.push("exports.push([module.id, " + cssString + ", \"\", " + map + "]);");
8878
} else {
89-
result.push("exports.push([module.id, " + css + ", \"\"]);");
79+
result.push("exports.push([module.id, " + cssString + ", \"\"]);");
9080
}
9181
return "exports = module.exports = require(" + JSON.stringify(require.resolve("./cssToString")) + ")();\n" +
9282
result.join("\n");
9383
}
9484

85+
function parseImport(str) {
86+
var m = /^\s*url\s*\(([^\)]+|\s*"[^"]"\s*|\s*'[^']'\s*)\)\s*(.*)$/i.exec(str);
87+
if(!m) return;
88+
return {
89+
url: loaderUtils.parseString(m[1].trim()),
90+
media: m[2].split(/\s+/)
91+
};
92+
}
93+
9594
function extractImports(tree) {
9695
var results = [];
97-
var removes = [];
98-
for(var i = 1; i < tree.length; i++) {
99-
var rule = tree[i];
100-
if(rule[0] === "atrules" &&
101-
rule[1][0] === "atkeyword" &&
102-
rule[1][1][0] === "ident" &&
103-
rule[1][1][1] === "import") {
104-
var imp = {
105-
url: null,
106-
media: []
107-
};
108-
for(var j = 2; j < rule.length; j++) {
109-
var item = rule[j];
110-
if(item[0] === "string") {
111-
imp.url = loaderUtils.parseString(item[1]);
112-
} else if(item[0] === "uri") {
113-
imp.url = item[1][0] === "string" ? loaderUtils.parseString(item[1][1]) : item[1][1];
114-
} else if(item[0] === "ident" && item[1] !== "url") {
115-
imp.media.push(csso.translate(item));
116-
} else if(item[0] !== "s" || imp.media.length > 0) {
117-
imp.media.push(csso.translate(item));
118-
}
119-
}
120-
while(imp.media.length > 0 &&
121-
/^\s*$/.test(imp.media[imp.media.length-1]))
122-
imp.media.pop();
123-
if(imp.url !== null) {
124-
results.push(imp);
125-
removes.push(i);
126-
}
96+
for(var i = 0; i < tree.rules.length; i++) {
97+
var rule = tree.rules[i];
98+
if(rule.type === "import") {
99+
var imp = parseImport(rule.import);
100+
if(!imp) continue;
101+
results.push(imp);
102+
tree.rules.splice(i, 1);
103+
i--;
127104
}
128105
}
129-
removes.reverse().forEach(function(i) {
130-
tree.splice(i, 1);
131-
});
132106
return results;
133107
}
108+
134109
function annotateUrls(tree) {
135-
function iterateChildren() {
136-
for(var i = 1; i < tree.length; i++) {
137-
annotateUrls(tree[i]);
110+
function iterateChildren(children) {
111+
for(var i = 0; i < children.length; i++) {
112+
annotateUrls(children[i]);
138113
}
139114
}
140-
switch(tree[0]) {
141-
case "stylesheet": return iterateChildren();
142-
case "ruleset": return iterateChildren();
143-
case "block": return iterateChildren();
144-
case "atruleb": return iterateChildren();
145-
case "atruler": return iterateChildren();
146-
case "atrulers": return iterateChildren();
147-
case "declaration": return iterateChildren();
148-
case "value": return iterateChildren();
149-
case "uri":
150-
for(var i = 1; i < tree.length; i++) {
151-
var item = tree[i];
152-
switch(item[0]) {
153-
case "ident":
154-
case "raw":
155-
item[1] = "%CSSURL[%" + item[1] + "%]CSSURL%";
156-
return;
157-
case "string":
158-
item[1] = "%CSSURL[%" + item[1] + "%]CSSURL%";
159-
return;
160-
}
161-
}
115+
switch(tree.type) {
116+
case "stylesheet": return iterateChildren(tree.stylesheet.rules);
117+
case "rule": return iterateChildren(tree.declarations);
118+
case "document": return iterateChildren(tree.rules);
119+
case "font-face": return iterateChildren(tree.declarations);
120+
case "host": return iterateChildren(tree.rules);
121+
case "keyframes": return iterateChildren(tree.keyframes);
122+
case "keyframe": return iterateChildren(tree.declarations);
123+
case "media": return iterateChildren(tree.rules);
124+
case "page": return iterateChildren(tree.declarations);
125+
case "supports": return iterateChildren(tree.rules);
126+
case "declaration":
127+
tree.value = tree.value.replace(/url\s*\(([^\)]+|\s*"[^"]"\s*|\s*'[^']'\s*)\)/ig, function(match) {
128+
var m = /^url\s*\(([^\)]+|\s*"[^"]"\s*|\s*'[^']'\s*)\)$/i.exec(match);
129+
return "url(%CSSURL[%" + m[1].trim() + "%]CSSURL%)";
130+
});
162131
}
163132
}
164133

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"author": "Tobias Koppers @sokra",
55
"description": "css loader module for webpack",
66
"dependencies": {
7-
"csso": "1.3.x",
7+
"css": "^2.1.0",
88
"source-map": "~0.1.38",
99
"loader-utils": "~0.2.2"
1010
},

test/urlTest.js

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,27 +60,27 @@ describe("url", function() {
6060
[1, "", ""]
6161
]);
6262
test("simple", ".class { a: b c d; }", [
63-
[1, ".class { a: b c d; }", ""]
63+
[1, ".class {\n a: b c d;\n}", ""]
6464
]);
65-
test("simple2", ".class { a: b c d; }\n.two {}", [
66-
[1, ".class { a: b c d; }\n.two {}", ""]
65+
test("simple2", ".class { a: b c d; }\n.two {t:2}", [
66+
[1, ".class {\n a: b c d;\n}\n\n.two {\n t: 2;\n}", ""]
6767
]);
6868
test("import", "@import url(test.css);\n.class { a: b c d; }", [
69-
[2, ".test{a: b}", ""],
70-
[1, "\n.class { a: b c d; }", ""]
69+
[2, ".test{ a: b }", ""],
70+
[1, ".class {\n a: b c d;\n}", ""]
7171
], "", {
72-
"./test.css": [[2, ".test{a: b}", ""]]
72+
"./test.css": [[2, ".test{ a: b }", ""]]
7373
});
7474
test("import 2", "@import url('test.css');\n.class { a: b c d; }", [
7575
[2, ".test{a: b}", "screen"],
76-
[1, "\n.class { a: b c d; }", ""]
76+
[1, ".class {\n a: b c d;\n}", ""]
7777
], "", {
7878
"./test.css": [[2, ".test{a: b}", "screen"]]
7979
});
8080
test("import with media", "@import url('~test/css') screen and print;\n.class { a: b c d; }", [
8181
[3, ".test{a: b}", "((min-width: 100px)) and (screen and print)"],
8282
[2, ".test{c: d}", "screen and print"],
83-
[1, "\n.class { a: b c d; }", ""]
83+
[1, ".class {\n a: b c d;\n}", ""]
8484
], "", {
8585
"test/css": [
8686
[3, ".test{a: b}", "(min-width: 100px)"],
@@ -90,42 +90,46 @@ describe("url", function() {
9090
test("import external", "@import url(http://example.com/style.css);\n@import url(\"//example.com/style.css\");", [
9191
[1, "@import url(http://example.com/style.css);", ""],
9292
[1, "@import url(//example.com/style.css);", ""],
93-
[1, "\n", ""]
93+
[1, "", ""]
9494
]);
9595
test("background img", ".class { background: green url( \"img.png\" ) xyz }", [
96-
[1, ".class { background: green url( {./img.png} ) xyz }", ""]
96+
[1, ".class {\n background: green url({./img.png}) xyz;\n}", ""]
9797
]);
9898
test("background img 2", ".class { background: green url(~img/png) url(aaa) xyz }", [
99-
[1, ".class { background: green url({img/png}) url({./aaa}) xyz }", ""]
99+
[1, ".class {\n background: green url({img/png}) url({./aaa}) xyz;\n}", ""]
100100
]);
101101
test("background img 3", ".class { background: green url( 'img.png' ) xyz }", [
102-
[1, ".class { background: green url( {./img.png} ) xyz }", ""]
102+
[1, ".class {\n background: green url({./img.png}) xyz;\n}", ""]
103103
]);
104104
test("background img absolute", ".class { background: green url(/img.png) xyz }", [
105-
[1, ".class { background: green url(/img.png) xyz }", ""]
105+
[1, ".class {\n background: green url(/img.png) xyz;\n}", ""]
106106
]);
107107
test("background img absolute with root", ".class { background: green url(/img.png) xyz }", [
108-
[1, ".class { background: green url({./img.png}) xyz }", ""]
108+
[1, ".class {\n background: green url({./img.png}) xyz;\n}", ""]
109109
], "?root=.");
110110
test("background img external",
111111
".class { background: green url(data:image/png;base64,AAA) url(http://example.com/image.jpg) url(//example.com/image.png) xyz }", [
112-
[1, ".class { background: green url(data:image/png;base64,AAA) url(http://example.com/image.jpg) url(//example.com/image.png) xyz }", ""]
112+
[1, ".class {\n background: green url(data:image/png;base64,AAA) url(http://example.com/image.jpg) url(//example.com/image.png) xyz;\n}", ""]
113113
]);
114114
test("background img external data",
115115
".class { background-image: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 42 26' fill='%23007aff'><rect width='4' height='4'/><rect x='8' y='1' width='34' height='2'/><rect y='11' width='4' height='4'/><rect x='8' y='12' width='34' height='2'/><rect y='22' width='4' height='4'/><rect x='8' y='23' width='34' height='2'/></svg>\") }", [
116-
[1, ".class { background-image: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 42 26' fill='%23007aff'><rect width='4' height='4'/><rect x='8' y='1' width='34' height='2'/><rect y='11' width='4' height='4'/><rect x='8' y='12' width='34' height='2'/><rect y='22' width='4' height='4'/><rect x='8' y='23' width='34' height='2'/></svg>\") }", ""]
116+
[1, ".class {\n background-image: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 42 26' fill='%23007aff'><rect width='4' height='4'/><rect x='8' y='1' width='34' height='2'/><rect y='11' width='4' height='4'/><rect x='8' y='12' width='34' height='2'/><rect y='22' width='4' height='4'/><rect x='8' y='23' width='34' height='2'/></svg>\");\n}", ""]
117117
]);
118118
test("filter hash",
119119
".highlight { filter: url(#highlight); }", [
120-
[1, ".highlight { filter: url(#highlight); }", ""]
120+
[1, ".highlight {\n filter: url(#highlight);\n}", ""]
121+
]);
122+
test("keyframes with semicolon",
123+
"@keyframes animation{ 0% { background-color: rgba(204, 204, 204, 0); } 50% { background-color: #cccccc; } 100% { width: 250px; height: 250px; background-color: rgba(204, 204, 204, 0); }}", [
124+
[1, "@keyframes animation {\n 0% {\n background-color: rgba(204, 204, 204, 0);\n }\n\n 50% {\n background-color: #cccccc;\n }\n\n 100% {\n width: 250px;\n height: 250px;\n background-color: rgba(204, 204, 204, 0);\n }\n}", ""]
121125
]);
122126
test("font face", "@font-face { src: url(regular.woff) format('woff'), url(~truetype/regular.ttf) format('truetype') }", [
123-
[1, "@font-face { src: url({./regular.woff}) format('woff'), url({truetype/regular.ttf}) format('truetype') }", ""]
127+
[1, "@font-face {\n src: url({./regular.woff}) format('woff'), url({truetype/regular.ttf}) format('truetype');\n}", ""]
124128
]);
125129
test("media query", "@media (min-width: 500px) { body { background: url(image.png); } }", [
126-
[1, "@media (min-width: 500px) { body { background: url({./image.png}); } }", ""]
130+
[1, "@media (min-width: 500px) {\n body {\n background: url({./image.png});\n }\n}", ""]
127131
]);
128132
testMinimize("minimized simple", ".class { a: b c d; }", [
129-
[1, ".class{a:b c d}", ""]
133+
[1, ".class{a:b c d;}", ""]
130134
]);
131135
});

0 commit comments

Comments
 (0)