Skip to content

Commit 8a6ea10

Browse files
authoredDec 4, 2018
refactor: postcss plugins (#844)
1 parent fdcf687 commit 8a6ea10

File tree

11 files changed

+140
-66
lines changed

11 files changed

+140
-66
lines changed
 

‎README.md

+16-6
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ url(image.png) => require('./image.png')
121121
url(./image.png) => require('./image.png')
122122
```
123123

124-
To import styles from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
124+
To import assets from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
125125

126126
```
127127
url(~module/image.png) => require('module/image.png')
@@ -130,14 +130,24 @@ url(~aliasDirectory/image.png) => require('otherDirectory/image.png')
130130

131131
### `import`
132132

133-
To disable `@import` resolving by `css-loader` set the option to `false`.
133+
Enable/disable `@import` resolving. Absolute urls are not resolving.
134+
135+
Examples resolutions:
134136

135-
Absolute urls are not resolving.
137+
```
138+
@import 'style.css' => require('./style.css')
139+
@import url(style.css) => require('./style.css')
140+
@import url('style.css') => require('./style.css')
141+
@import './style.css' => require('./style.css')
142+
@import url(./style.css) => require('./style.css')
143+
@import url('./style.css') => require('./style.css')
144+
```
136145

137-
To import styles from a node module path, prefix it with a `~`:
146+
To import styles from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
138147

139-
```css
140-
@import '~module/styles.css';
148+
```
149+
@import url(~module/style.css) => require('module/style.css')
150+
@import url(~aliasDirectory/style.css) => require('otherDirectory/style.css')
141151
```
142152

143153
### [`modules`](https://github.com/css-modules/css-modules)

‎lib/loader.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ module.exports = function loader(content, map, meta) {
191191
)}, ${JSON.stringify(media)}]);`;
192192
}
193193

194-
const importUrl = importUrlPrefix + url;
194+
const importUrl = importUrlPrefix + urlToRequest(url);
195195

196196
return `exports.i(require(${stringifyRequest(
197197
this,

‎lib/plugins/postcss-import-parser.js

+59-55
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const postcss = require('postcss');
22
const valueParser = require('postcss-value-parser');
3-
const loaderUtils = require('loader-utils');
43

54
const pluginName = 'postcss-import-parser';
65

@@ -19,7 +18,7 @@ function getUrl(node) {
1918
return node.value;
2019
}
2120

22-
return '';
21+
return null;
2322
}
2423

2524
function parseImport(params) {
@@ -31,7 +30,7 @@ function parseImport(params) {
3130

3231
const url = getUrl(nodes[0]);
3332

34-
if (url.trim().length === 0) {
33+
if (!url || url.trim().length === 0) {
3534
return null;
3635
}
3736

@@ -44,61 +43,66 @@ function parseImport(params) {
4443
};
4544
}
4645

46+
function walkAtRules(css, result, filter) {
47+
const items = [];
48+
49+
css.walkAtRules(/^import$/i, (atRule) => {
50+
// Convert only top-level @import
51+
if (atRule.parent.type !== 'root') {
52+
return;
53+
}
54+
55+
if (atRule.nodes) {
56+
result.warn(
57+
"It looks like you didn't end your @import statement correctly. " +
58+
'Child nodes are attached to it.',
59+
{ node: atRule }
60+
);
61+
return;
62+
}
63+
64+
const parsed = parseImport(atRule.params);
65+
66+
if (!parsed) {
67+
// eslint-disable-next-line consistent-return
68+
return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
69+
node: atRule,
70+
});
71+
}
72+
73+
if (filter && !filter(parsed)) {
74+
return;
75+
}
76+
77+
atRule.remove();
78+
79+
const { url, media } = parsed;
80+
81+
items.push({ url, media });
82+
});
83+
84+
return items;
85+
}
86+
87+
function uniq(array) {
88+
return array.reduce(
89+
(acc, d) =>
90+
!acc.find((el) => el.url === d.url && el.media === d.media)
91+
? [...acc, d]
92+
: acc,
93+
[]
94+
);
95+
}
96+
4797
module.exports = postcss.plugin(
4898
pluginName,
49-
() =>
99+
(options = {}) =>
50100
function process(css, result) {
51-
css.walkAtRules(/^import$/i, (atRule) => {
52-
// Convert only top-level @import
53-
if (atRule.parent.type !== 'root') {
54-
return;
55-
}
56-
57-
if (atRule.nodes) {
58-
result.warn(
59-
"It looks like you didn't end your @import statement correctly. " +
60-
'Child nodes are attached to it.',
61-
{ node: atRule }
62-
);
63-
return;
64-
}
65-
66-
const parsed = parseImport(atRule.params);
67-
68-
if (!parsed) {
69-
// eslint-disable-next-line consistent-return
70-
return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
71-
node: atRule,
72-
});
73-
}
74-
75-
atRule.remove();
76-
77-
const { media } = parsed;
78-
let { url } = parsed;
79-
const isUrlRequest = loaderUtils.isUrlRequest(url);
80-
81-
if (isUrlRequest) {
82-
url = loaderUtils.urlToRequest(url);
83-
}
84-
85-
const alreadyIncluded = result.messages.find(
86-
(message) =>
87-
message.pluginName === pluginName &&
88-
message.type === 'import' &&
89-
message.item.url === url &&
90-
message.item.media === media
91-
);
92-
93-
if (alreadyIncluded) {
94-
return;
95-
}
96-
97-
result.messages.push({
98-
pluginName,
99-
type: 'import',
100-
item: { url, media },
101-
});
101+
const traversed = walkAtRules(css, result, options.filter);
102+
const paths = uniq(traversed);
103+
104+
paths.forEach((item) => {
105+
result.messages.push({ pluginName, type: 'import', item });
102106
});
103107
}
104108
);

‎lib/plugins/postcss-url-parser.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function filterUrls(parsed, result, decl, filter) {
3939
return;
4040
}
4141

42-
if (!filter(url)) {
42+
if (filter && !filter(url)) {
4343
return;
4444
}
4545

@@ -96,7 +96,7 @@ function uniq(array) {
9696

9797
module.exports = postcss.plugin(
9898
pluginName,
99-
(options) =>
99+
(options = {}) =>
100100
function process(css, result) {
101101
const traversed = walkDeclsWithUrl(css, result, options.filter);
102102
const paths = uniq(flatten(traversed.map((item) => item.values)));
@@ -121,6 +121,7 @@ module.exports = postcss.plugin(
121121

122122
traversed.forEach((item) => {
123123
mapUrls(item.parsed, (value) => urls[value]);
124+
124125
// eslint-disable-next-line no-param-reassign
125126
item.decl.value = item.parsed.toString();
126127
});

‎test/__snapshots__/import-option.test.js.snap

+42-1
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,11 @@ Array [
343343
.foo {
344344
@import 'path.css';
345345
}
346+
347+
@import url('./relative.css');
348+
@import url('../import/top-relative.css');
349+
@import url(~package/tilde.css);
350+
@import url(~aliasesImport/alias.css);
346351
",
347352
"",
348353
],
@@ -355,7 +360,7 @@ exports[`import option false: module 1`] = `
355360
356361
357362
// module
358-
exports.push([module.id, \\"@import url(test.css);\\\\n@import url('test.css');\\\\n@import url(\\\\\\"test.css\\\\\\");\\\\n@IMPORT url(test.css);\\\\n@import URL(test.css);\\\\n@import url(test.css );\\\\n@import url( test.css);\\\\n@import url( test.css );\\\\n@import url(\\\\n test.css\\\\n);\\\\n@import url();\\\\n@import url('');\\\\n@import url(\\\\\\"\\\\\\");\\\\n@import \\\\\\"test.css\\\\\\";\\\\n@import 'test.css';\\\\n@import '';\\\\n@import \\\\\\"\\\\\\";\\\\n@import \\\\\\" \\\\\\";\\\\n@import \\\\\\"\\\\n\\\\\\";\\\\n@import url();\\\\n@import url('');\\\\n@import url(\\\\\\"\\\\\\");\\\\n@import url(test.css) screen and print;\\\\n@import url(test.css) SCREEN AND PRINT;\\\\n@import url(test.css)screen and print;\\\\n@import url(test.css) screen and print;\\\\n@import url(test-media.css) screen and print;\\\\n@import url(test-other.css) (min-width: 100px);\\\\n@import url(http://example.com/style.css);\\\\n@import url(http://example.com/style.css);\\\\n@import url(http://example.com/style.css#hash);\\\\n@import url(http://example.com/style.css?#hash);\\\\n@import url(http://example.com/style.css?foo=bar#hash);\\\\n@import url(http://example.com/other-style.css) screen and print;\\\\n@import url(http://example.com/other-style.css) screen and print;\\\\n@import url(\\\\\\"//example.com/style.css\\\\\\");\\\\n@import url(~package/test.css);\\\\n@import ;\\\\n@import foo-bar;\\\\n@import-normalize;\\\\n@import url('http://') :root {}\\\\n@import url('query.css?foo=1&bar=1');\\\\n@import url('other-query.css?foo=1&bar=1#hash');\\\\n@import url('other-query.css?foo=1&bar=1#hash') screen and print;\\\\n@import url('https://fonts.googleapis.com/css?family=Roboto');\\\\n@import url('https://fonts.googleapis.com/css?family=Noto+Sans+TC');\\\\n@import url('https://fonts.googleapis.com/css?family=Noto+Sans+TC|Roboto');\\\\n\\\\n.class {\\\\n a: b c d;\\\\n}\\\\n\\\\n.foo {\\\\n @import 'path.css';\\\\n}\\\\n\\", \\"\\"]);
363+
exports.push([module.id, \\"@import url(test.css);\\\\n@import url('test.css');\\\\n@import url(\\\\\\"test.css\\\\\\");\\\\n@IMPORT url(test.css);\\\\n@import URL(test.css);\\\\n@import url(test.css );\\\\n@import url( test.css);\\\\n@import url( test.css );\\\\n@import url(\\\\n test.css\\\\n);\\\\n@import url();\\\\n@import url('');\\\\n@import url(\\\\\\"\\\\\\");\\\\n@import \\\\\\"test.css\\\\\\";\\\\n@import 'test.css';\\\\n@import '';\\\\n@import \\\\\\"\\\\\\";\\\\n@import \\\\\\" \\\\\\";\\\\n@import \\\\\\"\\\\n\\\\\\";\\\\n@import url();\\\\n@import url('');\\\\n@import url(\\\\\\"\\\\\\");\\\\n@import url(test.css) screen and print;\\\\n@import url(test.css) SCREEN AND PRINT;\\\\n@import url(test.css)screen and print;\\\\n@import url(test.css) screen and print;\\\\n@import url(test-media.css) screen and print;\\\\n@import url(test-other.css) (min-width: 100px);\\\\n@import url(http://example.com/style.css);\\\\n@import url(http://example.com/style.css);\\\\n@import url(http://example.com/style.css#hash);\\\\n@import url(http://example.com/style.css?#hash);\\\\n@import url(http://example.com/style.css?foo=bar#hash);\\\\n@import url(http://example.com/other-style.css) screen and print;\\\\n@import url(http://example.com/other-style.css) screen and print;\\\\n@import url(\\\\\\"//example.com/style.css\\\\\\");\\\\n@import url(~package/test.css);\\\\n@import ;\\\\n@import foo-bar;\\\\n@import-normalize;\\\\n@import url('http://') :root {}\\\\n@import url('query.css?foo=1&bar=1');\\\\n@import url('other-query.css?foo=1&bar=1#hash');\\\\n@import url('other-query.css?foo=1&bar=1#hash') screen and print;\\\\n@import url('https://fonts.googleapis.com/css?family=Roboto');\\\\n@import url('https://fonts.googleapis.com/css?family=Noto+Sans+TC');\\\\n@import url('https://fonts.googleapis.com/css?family=Noto+Sans+TC|Roboto');\\\\n\\\\n.class {\\\\n a: b c d;\\\\n}\\\\n\\\\n.foo {\\\\n @import 'path.css';\\\\n}\\\\n\\\\n@import url('./relative.css');\\\\n@import url('../import/top-relative.css');\\\\n@import url(~package/tilde.css);\\\\n@import url(~aliasesImport/alias.css);\\\\n\\", \\"\\"]);
359364
360365
// exports
361366
"
@@ -476,6 +481,38 @@ Array [
476481
"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC|Roboto);",
477482
"",
478483
],
484+
Array [
485+
10,
486+
".relative {
487+
color: red;
488+
}
489+
",
490+
"",
491+
],
492+
Array [
493+
11,
494+
".top-relative {
495+
color: black;
496+
}
497+
",
498+
"",
499+
],
500+
Array [
501+
12,
502+
".tilde {
503+
color: yellow;
504+
}
505+
",
506+
"",
507+
],
508+
Array [
509+
13,
510+
".alias {
511+
color: red;
512+
}
513+
",
514+
"",
515+
],
479516
Array [
480517
1,
481518
"@import url();
@@ -527,6 +564,10 @@ exports.i(require(\\"-!../../../index.js??ref--4-0!./other-query.css?foo=1&bar=1
527564
exports.push([module.id, \\"@import url(https://fonts.googleapis.com/css?family=Roboto);\\", \\"\\"]);
528565
exports.push([module.id, \\"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC);\\", \\"\\"]);
529566
exports.push([module.id, \\"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC|Roboto);\\", \\"\\"]);
567+
exports.i(require(\\"-!../../../index.js??ref--4-0!./relative.css\\"), \\"\\");
568+
exports.i(require(\\"-!../../../index.js??ref--4-0!../import/top-relative.css\\"), \\"\\");
569+
exports.i(require(\\"-!../../../index.js??ref--4-0!package/tilde.css\\"), \\"\\");
570+
exports.i(require(\\"-!../../../index.js??ref--4-0!aliasesImport/alias.css\\"), \\"\\");
530571
531572
// module
532573
exports.push([module.id, \\"@import url();\\\\n@import url('');\\\\n@import url(\\\\\\"\\\\\\");\\\\n@import '';\\\\n@import \\\\\\"\\\\\\";\\\\n@import \\\\\\" \\\\\\";\\\\n@import \\\\\\"\\\\n\\\\\\";\\\\n@import url();\\\\n@import url('');\\\\n@import url(\\\\\\"\\\\\\");\\\\n@import ;\\\\n@import foo-bar;\\\\n@import-normalize;\\\\n@import url('http://') :root {}\\\\n\\\\n.class {\\\\n a: b c d;\\\\n}\\\\n\\\\n.foo {\\\\n @import 'path.css';\\\\n}\\\\n\\", \\"\\"]);

‎test/fixtures/import/alias.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.alias {
2+
color: red;
3+
}

‎test/fixtures/import/import.css

+5
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,8 @@
5555
.foo {
5656
@import 'path.css';
5757
}
58+
59+
@import url('./relative.css');
60+
@import url('../import/top-relative.css');
61+
@import url(~package/tilde.css);
62+
@import url(~aliasesImport/alias.css);

‎test/fixtures/import/node_modules/package/tilde.css

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/fixtures/import/relative.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.relative {
2+
color: red;
3+
}

‎test/fixtures/import/top-relative.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.top-relative {
2+
color: black;
3+
}

‎test/helpers.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function evaluated(output, modules, moduleId = 1) {
5555
path.resolve(
5656
__dirname,
5757
`./fixtures/${importedPath}`,
58-
module.replace('aliasesImg/', '')
58+
module.replace('aliasesImg/', '').replace('aliasesImport/', '')
5959
)
6060
);
6161

@@ -173,6 +173,7 @@ function compile(fixture, config = {}, options = {}) {
173173
resolve: {
174174
alias: {
175175
aliasesImg: path.resolve(__dirname, 'fixtures/url'),
176+
aliasesImport: path.resolve(__dirname, 'fixtures/import'),
176177
},
177178
},
178179
};

0 commit comments

Comments
 (0)
Please sign in to comment.