Skip to content

Commit 872cf94

Browse files
feat(plugins/import): add @import filter support (options.import) (#656)
1 parent d2332f9 commit 872cf94

17 files changed

+168
-71
lines changed

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ To disable `url()` resolving by `css-loader` set the option to `false`
103103

104104
#### `{Function}`
105105

106+
106107
**webpack.config.js**
107108
```js
108109
{
@@ -145,6 +146,32 @@ To disable `@import` resolving by `css-loader` set the option to `false`
145146
}
146147
```
147148

149+
#### `{RegExp}`
150+
151+
**webpack.config.js**
152+
```js
153+
{
154+
loader: 'css-loader',
155+
options: {
156+
import: /filter/
157+
}
158+
}
159+
```
160+
161+
#### `{Function}`
162+
163+
**webpack.config.js**
164+
```js
165+
{
166+
loader: 'css-loader',
167+
options: {
168+
import (url) {
169+
return /filter/.test(url)
170+
}
171+
}
172+
}
173+
```
174+
148175
### `minimize`
149176

150177
#### `{Boolean}`

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
"defaults": "webpack-defaults"
3333
},
3434
"dependencies": {
35-
"cssnano": "^4.0.0-rc.2",
3635
"loader-utils": "^1.1.0",
3736
"postcss": "^6.0.15",
3837
"postcss-value-parser": "^3.3.0",

src/index.js

+16-19
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import postcss from 'postcss';
1313
// replace with postcss-icss-{url, import}
1414
import urls from './plugins/url';
1515
import imports from './plugins/import';
16-
import minifier from 'cssnano';
1716

1817
// import runtime from './runtime';
1918
import SyntaxError from './Error';
@@ -22,20 +21,19 @@ import SyntaxError from './Error';
2221
const DEFAULTS = {
2322
url: true,
2423
import: true,
25-
minimize: false,
2624
sourceMap: false,
2725
};
2826

2927
export default function loader(css, map, meta) {
28+
// Loader Mode (Async)
29+
const cb = this.async();
30+
const file = this.resourcePath;
31+
3032
// Loader Options
3133
const options = Object.assign({}, DEFAULTS, getOptions(this));
3234

3335
validateOptions(schema, options, 'CSS Loader');
3436

35-
// Loader Mode (Async)
36-
const cb = this.async();
37-
const file = this.resourcePath;
38-
3937
if (options.sourceMap) {
4038
if (map && typeof map !== 'string') {
4139
map = JSON.stringify(map);
@@ -53,12 +51,7 @@ export default function loader(css, map, meta) {
5351

5452
// Import Plugin
5553
if (options.import) {
56-
plugins.push(imports());
57-
}
58-
59-
// Minifier
60-
if (options.minimize) {
61-
plugins.push(minifier());
54+
plugins.push(imports(options));
6255
}
6356

6457
if (meta) {
@@ -85,13 +78,13 @@ export default function loader(css, map, meta) {
8578
map,
8679
to: file,
8780
})
88-
.then(({ css, map, messages }) => {
81+
.then(({ root, css, map, messages }) => {
8982
if (meta && meta.messages) {
9083
messages = messages.concat(meta.messages);
9184
}
9285

9386
// CSS Imports
94-
const imports = messages
87+
let imports = messages
9588
.filter((msg) => (msg.type === 'import' ? msg : false))
9689
.reduce((imports, msg) => {
9790
try {
@@ -108,7 +101,7 @@ export default function loader(css, map, meta) {
108101
}, '');
109102

110103
// CSS Exports
111-
const exports = messages
104+
let exports = messages
112105
.filter((msg) => (msg.type === 'export' ? msg : false))
113106
.reduce((exports, msg) => {
114107
try {
@@ -123,17 +116,21 @@ export default function loader(css, map, meta) {
123116

124117
return exports;
125118
}, '');
119+
120+
imports = imports ? `// CSS Imports\n${imports}\n` : false
121+
exports = exports ? `// CSS Exports\n${exports}\n` : false
122+
css = `// CSS\nexport default \`${css}\``
126123

127124
// TODO(michael-ciniawsky)
128125
// triage if and add CSS runtime back
129126
const result = [
130-
imports ? `// CSS Imports\n${imports}\n` : false,
131-
exports ? `// CSS Exports\n${exports}\n` : false,
132-
`// CSS\nexport default \`${css}\``,
127+
imports,
128+
exports,
129+
css
133130
]
134131
.filter(Boolean)
135132
.join('\n');
136-
133+
137134
cb(null, result, map ? map.toJSON() : null);
138135

139136
return null;

src/options.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010
]
1111
},
1212
"import": {
13-
"type": "boolean"
13+
"anyOf": [
14+
{ "type": "string" },
15+
{ "type": "boolean" },
16+
{ "instanceof": "RegExp" },
17+
{ "instanceof": "Function" }
18+
]
1419
},
1520
"minimize": {
1621
"type": "boolean"

src/plugins/import.js

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/* eslint-disable */
22
import postcss from 'postcss';
33
import valueParser from 'postcss-value-parser';
4-
// ICSS {String}
5-
// import { createICSSRules } from "icss-utils";
4+
5+
const plugin = 'postcss-icss-import';
66

77
const getArg = nodes =>
88
(nodes.length !== 0 && nodes[0].type === 'string'
@@ -38,19 +38,39 @@ const parseImport = (params) => {
3838
};
3939
};
4040

41-
const isExternalUrl = url => /^\w+:\/\//.test(url) || url.startsWith('//');
41+
const URL = /^\w+:\/\//;
42+
43+
const filter = (url, options) => {
44+
if (URL.test(url)) {
45+
return true;
46+
}
47+
48+
if (url.startsWith('//')) {
49+
return true;
50+
}
51+
52+
if (options.import instanceof RegExp) {
53+
return options.import.test(url);
54+
}
55+
56+
if (typeof options.import === 'function') {
57+
return options.import(url);
58+
}
4259

43-
const walkImports = (css, callback) => {
60+
return false;
61+
}
62+
63+
const walkImports = (css, cb) => {
4464
css.each((node) => {
4565
if (node.type === 'atrule' && node.name.toLowerCase() === 'import') {
46-
callback(node);
66+
cb(node);
4767
}
4868
});
4969
};
5070

51-
const plugin = 'postcss-icss-import';
71+
export default postcss.plugin(plugin, (options) => (css, result) => {
72+
let idx = 0;
5273

53-
export default postcss.plugin(plugin, () => (css, result) => {
5474
walkImports(css, (atrule) => {
5575
if (atrule.nodes) {
5676
return result.warn(
@@ -62,15 +82,15 @@ export default postcss.plugin(plugin, () => (css, result) => {
6282
const parsed = parseImport(atrule.params);
6383

6484
if (parsed === null) {
65-
return result.warn(`Unable to find uri in '${atrule.toString()}'`, {
85+
return result.warn(`Unable to find URI in '${atrule.toString()}'`, {
6686
node: atrule,
6787
});
6888
}
6989

7090
let idx = 0;
7191
const url = parsed.url;
7292

73-
if (!isExternalUrl(url)) {
93+
if (!filter(url, options)) {
7494
atrule.remove();
7595

7696
result.messages.push({
@@ -82,4 +102,4 @@ export default postcss.plugin(plugin, () => (css, result) => {
82102
idx++;
83103
}
84104
});
85-
});
105+
});

test/Errors.test.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,21 @@ describe('Errors', () => {
2323
expect(err).toThrowErrorMatchingSnapshot();
2424
});
2525

26-
test('Validation Error', () => {
27-
const err = () => loader.call({ query: { sourceMap: 1 } });
26+
test('Validation Error', async () => {
27+
const config = {
28+
loader: {
29+
test: /\.css$/,
30+
options: {
31+
sourceMap: 1
32+
},
33+
},
34+
};
35+
36+
const stats = await webpack('error.js', config);
37+
const { source } = stats.toJson().modules[1];
38+
39+
// eslint-disable-next-line
40+
const err = () => eval(source);
2841

2942
expect(err).toThrow();
3043
expect(err).toThrowErrorMatchingSnapshot();

test/__snapshots__/Errors.test.js.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ exports[`Errors Loader Error 1`] = `
1313
`;
1414
1515
exports[`Errors Validation Error 1`] = `
16-
"CSS Loader Invalid Options
16+
"Module build failed: ValidationError: CSS Loader Invalid Options
1717
1818
options.sourceMap should be boolean
1919
"
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@import './import.css';
2+
@import './filter/import.css';
3+
4+
.css {
5+
width: 100%;
6+
}
File renamed without changes.

test/fixtures/import/fixture.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import css from './fixture.css';
2+
3+
export default css;
File renamed without changes.

test/options/__snapshots__/import.test.js.snap

+28
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,31 @@ export default \`.fixture {
1111
}
1212
\`"
1313
`;
14+
15+
exports[`Options import {Function} 1`] = `
16+
"// CSS Imports
17+
import CSS__IMPORT__0 from './import.css';
18+
19+
20+
// CSS
21+
export default \`@import './filter/import.css';
22+
23+
.css {
24+
width: 100%;
25+
}
26+
\`"
27+
`;
28+
29+
exports[`Options import {RegExp} 1`] = `
30+
"// CSS Imports
31+
import CSS__IMPORT__0 from './import.css';
32+
33+
34+
// CSS
35+
export default \`@import './filter/import.css';
36+
37+
.css {
38+
width: 100%;
39+
}
40+
\`"
41+
`;

test/options/__snapshots__/minimize.test.js.snap

-11
This file was deleted.

test/options/import.test.js

+35-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,41 @@ describe('Options', () => {
1313
},
1414
};
1515

16-
const stats = await webpack('imports/fixture.js', config);
16+
const stats = await webpack('import/fixture.js', config);
17+
const { source } = stats.toJson().modules[1];
18+
19+
expect(source).toMatchSnapshot();
20+
});
21+
22+
test('{RegExp}', async () => {
23+
const config = {
24+
loader: {
25+
test: /\.css$/,
26+
options: {
27+
import: /filter/,
28+
},
29+
},
30+
};
31+
32+
const stats = await webpack('import/filter/fixture.js', config);
33+
const { source } = stats.toJson().modules[1];
34+
35+
expect(source).toMatchSnapshot();
36+
});
37+
38+
test('{Function}', async () => {
39+
const config = {
40+
loader: {
41+
test: /\.css$/,
42+
options: {
43+
import(url) {
44+
return /filter/.test(url);
45+
},
46+
},
47+
},
48+
};
49+
50+
const stats = await webpack('import/filter/fixture.js', config);
1751
const { source } = stats.toJson().modules[1];
1852

1953
expect(source).toMatchSnapshot();

test/options/minimize.test.js

-24
This file was deleted.

0 commit comments

Comments
 (0)