Skip to content

Commit 5e6034c

Browse files
authoredDec 6, 2018
feat: allow to filter import at-rules (#857)
1 parent 5e702e7 commit 5e6034c

File tree

7 files changed

+375
-19
lines changed

7 files changed

+375
-19
lines changed
 

‎README.md

+61-17
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ module.exports = {
117117
| Name | Type | Default | Description |
118118
| :-----------------------------------------: | :-------------------: | :-------------: | :------------------------------------------ |
119119
| **[`url`](#url)** | `{Boolean\|Function}` | `true` | Enable/Disable `url()` handling |
120-
| **[`import`](#import)** | `{Boolean}` | `true` | Enable/Disable @import handling |
120+
| **[`import`](#import)** | `{Boolean\/Function}` | `true` | Enable/Disable @import handling |
121121
| **[`modules`](#modules)** | `{Boolean\|String}` | `false` | Enable/Disable CSS Modules and setup mode |
122122
| **[`localIdentName`](#localidentname)** | `{String}` | `[hash:base64]` | Configure the generated ident |
123123
| **[`sourceMap`](#sourcemap)** | `{Boolean}` | `false` | Enable/Disable Sourcemaps |
@@ -130,19 +130,24 @@ module.exports = {
130130
Type: `Boolean|Function`
131131
Default: `true`
132132

133-
Control `url()` resolving. Absolute `urls` are not resolving by default.
133+
Control `url()` resolving. Absolute urls are not resolving.
134134

135135
Examples resolutions:
136136

137137
```
138138
url(image.png) => require('./image.png')
139+
url('image.png') => require('./image.png')
139140
url(./image.png) => require('./image.png')
141+
url('./image.png') => require('./image.png')
142+
url('http://dontwritehorriblecode.com/2112.png') => require('http://dontwritehorriblecode.com/2112.png')
143+
image-set(url('image2x.png') 1x, url('image1x.png') 2x) => require('./image1x.png') and require('./image2x.png')
140144
```
141145

142146
To import assets from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
143147

144148
```
145149
url(~module/image.png) => require('module/image.png')
150+
url('~module/image.png') => require('module/image.png')
146151
url(~aliasDirectory/image.png) => require('otherDirectory/image.png')
147152
```
148153

@@ -170,7 +175,9 @@ module.exports = {
170175

171176
#### `Function`
172177

173-
Allow to filter `url()`. All filtered `url()` will not be resolved.
178+
Allow to filter `url()`. All filtered `url()` will not be resolved (left in the code as they were written).
179+
180+
**webpack.config.js**
174181

175182
```js
176183
module.exports = {
@@ -181,6 +188,8 @@ module.exports = {
181188
loader: 'css-loader',
182189
options: {
183190
url: (url, resourcePath) => {
191+
// resourcePath - path to css file
192+
184193
// `url()` with `img.png` stay untouched
185194
return url.includes('img.png');
186195
},
@@ -196,7 +205,31 @@ module.exports = {
196205
Type: `Boolean`
197206
Default: `true`
198207

199-
Enable/disable `@import` resolving. Absolute urls are not resolving.
208+
Control `@import` resolving. Absolute urls in `@import` will be moved in runtime code.
209+
210+
Examples resolutions:
211+
212+
```
213+
@import 'style.css' => require('./style.css')
214+
@import url(style.css) => require('./style.css')
215+
@import url('style.css') => require('./style.css')
216+
@import './style.css' => require('./style.css')
217+
@import url(./style.css) => require('./style.css')
218+
@import url('./style.css') => require('./style.css')
219+
@import url('http://dontwritehorriblecode.com/style.css') => @import url('http://dontwritehorriblecode.com/style.css') in runtime
220+
```
221+
222+
To import styles from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
223+
224+
```
225+
@import url(~module/style.css) => require('module/style.css')
226+
@import url('~module/style.css') => require('module/style.css')
227+
@import url(~aliasDirectory/style.css) => require('otherDirectory/style.css')
228+
```
229+
230+
#### `Boolean`
231+
232+
Enable/disable `@import` resolving.
200233

201234
**webpack.config.js**
202235

@@ -216,22 +249,33 @@ module.exports = {
216249
};
217250
```
218251

219-
Examples resolutions:
252+
#### `Function`
220253

221-
```
222-
@import 'style.css' => require('./style.css')
223-
@import url(style.css) => require('./style.css')
224-
@import url('style.css') => require('./style.css')
225-
@import './style.css' => require('./style.css')
226-
@import url(./style.css) => require('./style.css')
227-
@import url('./style.css') => require('./style.css')
228-
```
254+
Allow to filter `@import`. All filtered `@import` will not be resolved (left in the code as they were written).
229255

230-
To import styles from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
256+
**webpack.config.js**
231257

232-
```
233-
@import url(~module/style.css) => require('module/style.css')
234-
@import url(~aliasDirectory/style.css) => require('otherDirectory/style.css')
258+
```js
259+
module.exports = {
260+
module: {
261+
rules: [
262+
{
263+
test: /\.css$/,
264+
loader: 'css-loader',
265+
options: {
266+
import: (parsedImport, resourcePath) => {
267+
// parsedImport.url - url of `@import`
268+
// parsedImport.media - media query of `@import`
269+
// resourcePath - path to css file
270+
271+
// `@import` with `style.css` stay untouched
272+
return parsedImport.url.includes('style.css');
273+
},
274+
},
275+
},
276+
],
277+
},
278+
};
235279
```
236280

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

‎src/index.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ export default function loader(content, map, meta) {
9999
}
100100

101101
if (options.import !== false) {
102-
plugins.push(importParser());
102+
plugins.push(
103+
importParser({
104+
filter: getFilter(options.import, this.resourcePath),
105+
})
106+
);
103107
}
104108

105109
if (options.url !== false) {

‎src/options.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
]
1313
},
1414
"import": {
15-
"type": "boolean"
15+
"anyOf": [
16+
{
17+
"type": "boolean"
18+
},
19+
{
20+
"instanceof": "Function"
21+
}
22+
]
1623
},
1724
"modules": {
1825
"anyOf": [

‎test/__snapshots__/errors.test.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ exports[`validation 2`] = `
1313
"CSS Loader Invalid Options
1414
1515
options.import should be boolean
16+
options.import should pass \\"instanceof\\" keyword validation
17+
options.import should match some schema in anyOf
1618
"
1719
`;
1820

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

+271
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,276 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`import option Function: errors 1`] = `Array []`;
4+
5+
exports[`import option Function: module (evaluated) 1`] = `
6+
Array [
7+
Array [
8+
3,
9+
"a {
10+
b: b;
11+
}
12+
",
13+
"((min-width: 100px)) and (screen and print)",
14+
],
15+
Array [
16+
2,
17+
".test {
18+
c: c;
19+
}
20+
",
21+
"screen and print",
22+
],
23+
Array [
24+
1,
25+
"@import url(http://example.com/style.css);",
26+
"",
27+
],
28+
Array [
29+
1,
30+
"@import url(http://example.com/style.css#hash);",
31+
"",
32+
],
33+
Array [
34+
1,
35+
"@import url(http://example.com/style.css?#hash);",
36+
"",
37+
],
38+
Array [
39+
1,
40+
"@import url(http://example.com/style.css?foo=bar#hash);",
41+
"",
42+
],
43+
Array [
44+
1,
45+
"@import url(http://example.com/other-style.css);",
46+
"screen and print",
47+
],
48+
Array [
49+
1,
50+
"@import url(//example.com/style.css);",
51+
"",
52+
],
53+
Array [
54+
4,
55+
".query {
56+
e: e;
57+
}
58+
",
59+
"",
60+
],
61+
Array [
62+
5,
63+
".other-query {
64+
f: f;
65+
}
66+
",
67+
"",
68+
],
69+
Array [
70+
6,
71+
".other-query {
72+
f: f;
73+
}
74+
",
75+
"screen and print",
76+
],
77+
Array [
78+
1,
79+
"@import url(https://fonts.googleapis.com/css?family=Roboto);",
80+
"",
81+
],
82+
Array [
83+
1,
84+
"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC);",
85+
"",
86+
],
87+
Array [
88+
1,
89+
"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC|Roboto);",
90+
"",
91+
],
92+
Array [
93+
7,
94+
".relative {
95+
color: red;
96+
}
97+
",
98+
"",
99+
],
100+
Array [
101+
8,
102+
".top-relative {
103+
color: black;
104+
}
105+
",
106+
"",
107+
],
108+
Array [
109+
9,
110+
".tilde {
111+
color: yellow;
112+
}
113+
",
114+
"",
115+
],
116+
Array [
117+
10,
118+
".alias {
119+
color: red;
120+
}
121+
",
122+
"",
123+
],
124+
Array [
125+
11,
126+
".background-imported {
127+
background: url(/webpack/public/path/img.png);
128+
}
129+
",
130+
"",
131+
],
132+
Array [
133+
1,
134+
"@import url(test.css);
135+
@import url('test.css');
136+
@import url(\\"test.css\\");
137+
@IMPORT url(test.css);
138+
@import URL(test.css);
139+
@import url(test.css );
140+
@import url( test.css);
141+
@import url( test.css );
142+
@import url(
143+
test.css
144+
);
145+
@import url();
146+
@import url('');
147+
@import url(\\"\\");
148+
@import \\"test.css\\";
149+
@import 'test.css';
150+
@import '';
151+
@import \\"\\";
152+
@import \\" \\";
153+
@import \\"
154+
\\";
155+
@import url();
156+
@import url('');
157+
@import url(\\"\\");
158+
@import url(test.css) screen and print;
159+
@import url(test.css) SCREEN AND PRINT;
160+
@import url(test.css)screen and print;
161+
@import url(test.css) screen and print;
162+
@import url(~package/test.css);
163+
@import ;
164+
@import foo-bar;
165+
@import-normalize;
166+
@import url('http://') :root {}
167+
168+
.class {
169+
a: b c d;
170+
}
171+
172+
.foo {
173+
@import 'path.css';
174+
}
175+
176+
.background {
177+
background: url(/webpack/public/path/img.png);
178+
}
179+
",
180+
"",
181+
],
182+
]
183+
`;
184+
185+
exports[`import option Function: module 1`] = `
186+
"exports = module.exports = require(\\"../../../src/runtime/api.js\\")(false);
187+
// Imports
188+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./test-media.css\\"), \\"screen and print\\");
189+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./test-other.css\\"), \\"(min-width: 100px)\\");
190+
exports.push([module.id, \\"@import url(http://example.com/style.css);\\", \\"\\"]);
191+
exports.push([module.id, \\"@import url(http://example.com/style.css#hash);\\", \\"\\"]);
192+
exports.push([module.id, \\"@import url(http://example.com/style.css?#hash);\\", \\"\\"]);
193+
exports.push([module.id, \\"@import url(http://example.com/style.css?foo=bar#hash);\\", \\"\\"]);
194+
exports.push([module.id, \\"@import url(http://example.com/other-style.css);\\", \\"screen and print\\"]);
195+
exports.push([module.id, \\"@import url(//example.com/style.css);\\", \\"\\"]);
196+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./query.css?foo=1&bar=1\\"), \\"\\");
197+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./other-query.css?foo=1&bar=1#hash\\"), \\"\\");
198+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./other-query.css?foo=1&bar=1#hash\\"), \\"screen and print\\");
199+
exports.push([module.id, \\"@import url(https://fonts.googleapis.com/css?family=Roboto);\\", \\"\\"]);
200+
exports.push([module.id, \\"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC);\\", \\"\\"]);
201+
exports.push([module.id, \\"@import url(https://fonts.googleapis.com/css?family=Noto+Sans+TC|Roboto);\\", \\"\\"]);
202+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./relative.css\\"), \\"\\");
203+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!../import/top-relative.css\\"), \\"\\");
204+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!package/tilde.css\\"), \\"\\");
205+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!aliasesImport/alias.css\\"), \\"\\");
206+
exports.i(require(\\"-!../../../src/index.js??ref--4-0!./url.css\\"), \\"\\");
207+
var urlEscape = require(\\"../../../src/runtime/url-escape.js\\");
208+
var ___CSS_LOADER_URL___0___ = urlEscape(require(\\"./img.png\\"));
209+
210+
// Module
211+
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(~package/test.css);\\\\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\\\\n.background {\\\\n background: url(\\" + ___CSS_LOADER_URL___0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
212+
213+
"
214+
`;
215+
216+
exports[`import option Function: warnings 1`] = `
217+
Array [
218+
"ModuleWarning: Module Warning (from \`replaced original path\`):
219+
Warning
220+
221+
(12:1) Unable to find uri in '@import url()'",
222+
"ModuleWarning: Module Warning (from \`replaced original path\`):
223+
Warning
224+
225+
(13:1) Unable to find uri in '@import url('')'",
226+
"ModuleWarning: Module Warning (from \`replaced original path\`):
227+
Warning
228+
229+
(14:1) Unable to find uri in '@import url(\\"\\")'",
230+
"ModuleWarning: Module Warning (from \`replaced original path\`):
231+
Warning
232+
233+
(17:1) Unable to find uri in '@import '''",
234+
"ModuleWarning: Module Warning (from \`replaced original path\`):
235+
Warning
236+
237+
(18:1) Unable to find uri in '@import \\"\\"'",
238+
"ModuleWarning: Module Warning (from \`replaced original path\`):
239+
Warning
240+
241+
(19:1) Unable to find uri in '@import \\" \\"'",
242+
"ModuleWarning: Module Warning (from \`replaced original path\`):
243+
Warning
244+
245+
(20:1) Unable to find uri in '@import \\"
246+
\\"'",
247+
"ModuleWarning: Module Warning (from \`replaced original path\`):
248+
Warning
249+
250+
(22:1) Unable to find uri in '@import url()'",
251+
"ModuleWarning: Module Warning (from \`replaced original path\`):
252+
Warning
253+
254+
(23:1) Unable to find uri in '@import url('')'",
255+
"ModuleWarning: Module Warning (from \`replaced original path\`):
256+
Warning
257+
258+
(24:1) Unable to find uri in '@import url(\\"\\")'",
259+
"ModuleWarning: Module Warning (from \`replaced original path\`):
260+
Warning
261+
262+
(40:1) Unable to find uri in '@import '",
263+
"ModuleWarning: Module Warning (from \`replaced original path\`):
264+
Warning
265+
266+
(41:1) Unable to find uri in '@import foo-bar'",
267+
"ModuleWarning: Module Warning (from \`replaced original path\`):
268+
Warning
269+
270+
(43:1) It looks like you didn't end your @import statement correctly. Child nodes are attached to it.",
271+
]
272+
`;
273+
3274
exports[`import option false: errors 1`] = `Array []`;
4275

5276
exports[`import option false: module (evaluated) 1`] = `

‎test/errors.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ it('validation', () => {
2727

2828
expect(() => validate({ import: true })).not.toThrow();
2929
expect(() => validate({ import: false })).not.toThrow();
30+
expect(() => validate({ import: () => {} })).not.toThrow();
3031
expect(() => validate({ import: 'true' })).toThrowErrorMatchingSnapshot();
3132

3233
expect(() => validate({ modules: true })).not.toThrow();

‎test/import-option.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,31 @@ describe('import option', () => {
5454
);
5555
});
5656
});
57+
58+
it('Function', async () => {
59+
const config = {
60+
loader: {
61+
options: {
62+
import: (parsedImport, resourcePath) => {
63+
expect(typeof resourcePath === 'string').toBe(true);
64+
65+
return parsedImport.url.includes('test.css');
66+
},
67+
},
68+
},
69+
};
70+
const testId = './import/import.css';
71+
const stats = await webpack(testId, config);
72+
const { modules } = stats.toJson();
73+
const module = modules.find((m) => m.id === testId);
74+
75+
expect(module.source).toMatchSnapshot('module');
76+
expect(evaluated(module.source, modules)).toMatchSnapshot(
77+
'module (evaluated)'
78+
);
79+
expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot(
80+
'warnings'
81+
);
82+
expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors');
83+
});
5784
});

0 commit comments

Comments
 (0)
Please sign in to comment.