Skip to content

Commit 889dc7f

Browse files
feat: allow to disable css modules and disable their by default (#842)
BREAKING CHANGE: by default css modules are disabled (now `modules: false` disable all css modules features), you can return old behaviour change this on `modules: 'global'`
1 parent ee2d253 commit 889dc7f

12 files changed

+2905
-438
lines changed

README.md

+25-9
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ It's useful when you, for instance, need to post process the CSS as a string.
9797
|:--:|:--:|:-----:|:----------|
9898
|**[`url`](#url)**|`{Boolean}`|`true`| Enable/Disable `url()` handling|
9999
|**[`import`](#import)** |`{Boolean}`|`true`| Enable/Disable @import handling|
100-
|**[`modules`](#modules)**|`{Boolean}`|`false`|Enable/Disable CSS Modules|
100+
|**[`modules`](#modules)**|`{Boolean\|String}`|`false`|Enable/Disable CSS Modules and setup mode|
101101
|**[`localIdentName`](#localidentname)**|`{String}`|`[hash:base64]`|Configure the generated ident|
102102
|**[`sourceMap`](#sourcemap)**|`{Boolean}`|`false`|Enable/Disable Sourcemaps|
103103
|**[`camelCase`](#camelcase)**|`{Boolean\|String}`|`false`|Export Classnames in CamelCase|
@@ -129,17 +129,33 @@ To import styles from a node module path, prefix it with a `~`:
129129

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

132-
The query parameter `modules` enables the **CSS Modules** spec.
132+
The `modules` option enables/disables the **CSS Modules** spec and setup basic behaviour.
133133

134-
This enables local scoped CSS by default. (You can switch it off with `:global(...)` or `:global` for selectors and/or rules.).
134+
|Name|Type|Description|
135+
|:--:|:--:|:----------|
136+
|**`true`**|`{Boolean}`|Enables local scoped CSS by default (use **local** mode by default)|
137+
|**`false`**|`{Boolean}`|Disable the **CSS Modules** spec, all **CSS Modules** features (like `@value`, `:local`, `:global` and `composes`) will not work|
138+
|**`'local'`** |`{String}`|Enables local scoped CSS by default (same as `true` value)|
139+
|**`'global'`**|`{String}`|Enables global scoped CSS by default|
140+
141+
Using `false` value increase performance because we avoid parsing **CSS Modules** features, it will be useful for developers who use vanilla css or use other technologies.
142+
143+
You can read about **modes** below
135144

136-
#### `Scope`
145+
##### `Scope`
146+
147+
Using `local` value requires you to specify `:global` classes.
148+
Using `global` value requires you to specify `:local` classes.
137149

138-
By default CSS exports all classnames into a global selector scope. Styles can be locally scoped to avoid globally scoping styles.
150+
You can find more information [here](https://github.com/css-modules/css-modules).
151+
152+
Styles can be locally scoped to avoid globally scoping styles.
139153

140154
The syntax `:local(.className)` can be used to declare `className` in the local scope. The local identifiers are exported by the module.
141155

142-
With `:local` (without brackets) local mode can be switched on for this selector. `:global(.className)` can be used to declare an explicit global selector. With `:global` (without brackets) global mode can be switched on for this selector.
156+
With `:local` (without brackets) local mode can be switched on for this selector.
157+
The `:global(.className)` nocation can be used to declare an explicit global selector.
158+
With `:global` (without brackets) global mode can be switched on for this selector.
143159

144160
The loader replaces local selectors with unique identifiers. The chosen unique identifiers are exported by the module.
145161

@@ -177,7 +193,7 @@ file.png => ./file.png
177193

178194
You can use `:local(#someId)`, but this is not recommended. Use classes instead of ids.
179195

180-
#### `Composing`
196+
##### `Composing`
181197

182198
When declaring a local classname you can compose a local class from another local classname.
183199

@@ -213,7 +229,7 @@ exports.locals = {
213229
}
214230
```
215231

216-
#### `Importing`
232+
##### `Importing`
217233

218234
To import a local classname from another module.
219235

@@ -282,7 +298,7 @@ You can also specify the absolute path to your custom `getLocalIdent` function t
282298

283299
To include source maps set the `sourceMap` option.
284300

285-
I.e. the extract-text-webpack-plugin can handle them.
301+
I.e. the `mini-css-extract-plugin` can handle them.
286302

287303
They are not enabled by default because they expose a runtime overhead and increase in bundle size (JS source maps do not). In addition to that relative paths are buggy and you need to use an absolute public path which includes the server URL.
288304

lib/loader.js

+46-34
Original file line numberDiff line numberDiff line change
@@ -62,43 +62,55 @@ module.exports = function loader(content, map, meta) {
6262

6363
const resolveImport = options.import !== false;
6464
const resolveUrl = options.url !== false;
65-
const loaderContext = this;
66-
67-
const plugins = [
68-
modulesValues,
69-
localByDefault({
70-
mode: options.modules ? 'local' : 'global',
71-
rewriteUrl(global, url) {
72-
if (resolveUrl) {
73-
// eslint-disable-next-line no-param-reassign
74-
url = url.trim();
75-
76-
if (!url.replace(/\s/g, '').length || !isUrlRequest(url)) {
77-
return url;
78-
}
7965

80-
if (global) {
81-
return urlToRequest(url);
66+
const plugins = [];
67+
68+
if (options.modules) {
69+
const loaderContext = this;
70+
const mode =
71+
typeof options.modules === 'boolean' ? 'local' : options.modules;
72+
73+
plugins.push(
74+
modulesValues,
75+
localByDefault({
76+
mode,
77+
rewriteUrl(global, url) {
78+
if (resolveUrl) {
79+
// eslint-disable-next-line no-param-reassign
80+
url = url.trim();
81+
82+
if (!url.replace(/\s/g, '').length || !isUrlRequest(url)) {
83+
return url;
84+
}
85+
86+
if (global) {
87+
return urlToRequest(url);
88+
}
8289
}
83-
}
8490

85-
return url;
86-
},
87-
}),
88-
extractImports(),
89-
modulesScope({
90-
generateScopedName: function generateScopedName(exportName) {
91-
const localIdentName = options.localIdentName || '[hash:base64]';
92-
const customGetLocalIdent = options.getLocalIdent || getLocalIdent;
93-
94-
return customGetLocalIdent(loaderContext, localIdentName, exportName, {
95-
regExp: options.localIdentRegExp,
96-
hashPrefix: options.hashPrefix || '',
97-
context: options.context,
98-
});
99-
},
100-
}),
101-
];
91+
return url;
92+
},
93+
}),
94+
extractImports(),
95+
modulesScope({
96+
generateScopedName: function generateScopedName(exportName) {
97+
const localIdentName = options.localIdentName || '[hash:base64]';
98+
const customGetLocalIdent = options.getLocalIdent || getLocalIdent;
99+
100+
return customGetLocalIdent(
101+
loaderContext,
102+
localIdentName,
103+
exportName,
104+
{
105+
regExp: options.localIdentRegExp,
106+
hashPrefix: options.hashPrefix || '',
107+
context: options.context,
108+
}
109+
);
110+
},
111+
})
112+
);
113+
}
102114

103115
if (resolveImport) {
104116
plugins.push(importParser());

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

+142-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,67 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`import option false and modules false: errors 1`] = `Array []`;
3+
exports[`import option false and modules \`false\`: errors 1`] = `Array []`;
44

5-
exports[`import option false and modules false: module (evaluated) 1`] = `
5+
exports[`import option false and modules \`false\`: module (evaluated) 1`] = `
6+
Array [
7+
Array [
8+
1,
9+
"@import url(test-other.css) (min-width: 100px);
10+
11+
@value def from './values.css';
12+
@value other from './values.css';
13+
@value other from './values.css';
14+
@value something from './something.css';
15+
@value foo: blue;
16+
@value bar: block;
17+
18+
.ghi {
19+
color: def;
20+
}
21+
22+
.class {
23+
color: foo;
24+
}
25+
26+
.other {
27+
display: bar;
28+
}
29+
30+
.other-other {
31+
width: something;
32+
}
33+
34+
.green {
35+
color: other;
36+
}
37+
38+
.foo {
39+
prop: def;
40+
duplicate: other;
41+
}
42+
",
43+
"",
44+
],
45+
]
46+
`;
47+
48+
exports[`import option false and modules \`false\`: module 1`] = `
49+
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
50+
// imports
51+
52+
53+
// module
54+
exports.push([module.id, \\"@import url(test-other.css) (min-width: 100px);\\\\n\\\\n@value def from './values.css';\\\\n@value other from './values.css';\\\\n@value other from './values.css';\\\\n@value something from './something.css';\\\\n@value foo: blue;\\\\n@value bar: block;\\\\n\\\\n.ghi {\\\\n color: def;\\\\n}\\\\n\\\\n.class {\\\\n color: foo;\\\\n}\\\\n\\\\n.other {\\\\n display: bar;\\\\n}\\\\n\\\\n.other-other {\\\\n width: something;\\\\n}\\\\n\\\\n.green {\\\\n color: other;\\\\n}\\\\n\\\\n.foo {\\\\n prop: def;\\\\n duplicate: other;\\\\n}\\\\n\\", \\"\\"]);
55+
56+
// exports
57+
"
58+
`;
59+
60+
exports[`import option false and modules \`false\`: warnings 1`] = `Array []`;
61+
62+
exports[`import option false and modules \`global\`: errors 1`] = `Array []`;
63+
64+
exports[`import option false and modules \`global\`: module (evaluated) 1`] = `
665
Array [
766
Array [
867
2,
@@ -50,7 +109,7 @@ Array [
50109
]
51110
`;
52111

53-
exports[`import option false and modules false: module 1`] = `
112+
exports[`import option false and modules \`global\`: module 1`] = `
54113
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
55114
// imports
56115
exports.i(require(\\"-!../../../index.js??ref--4-0!./values.css\\"), \\"\\");
@@ -69,11 +128,86 @@ exports.locals = {
69128
};"
70129
`;
71130
72-
exports[`import option false and modules false: warnings 1`] = `Array []`;
131+
exports[`import option false and modules \`global\`: warnings 1`] = `Array []`;
132+
133+
exports[`import option false and modules \`local\`: errors 1`] = `Array []`;
134+
135+
exports[`import option false and modules \`local\`: module (evaluated) 1`] = `
136+
Array [
137+
Array [
138+
2,
139+
"
140+
",
141+
"",
142+
],
143+
Array [
144+
3,
145+
"
146+
",
147+
"",
148+
],
149+
Array [
150+
1,
151+
"@import url(test-other.css) (min-width: 100px);
152+
153+
._3r49KZIIAltPknAjdNVZ-7 {
154+
color: red;
155+
}
156+
157+
._4o0o5eKzoeDOSI0_cR8mr {
158+
color: blue;
159+
}
160+
161+
._2wLXKM9pRjt1oRYvf0Wo3Q {
162+
display: block;
163+
}
164+
165+
._1RBgqC8j3f4iU6k-ocmIG7 {
166+
width: 2112moon;
167+
}
168+
169+
._1lCIckG6C8tRZjGNDsAPWr {
170+
color: green;
171+
}
172+
173+
._1YL4f0i_603GTMRC_pnsP5 {
174+
prop: red;
175+
duplicate: green;
176+
}
177+
",
178+
"",
179+
],
180+
]
181+
`;
182+
183+
exports[`import option false and modules \`local\`: module 1`] = `
184+
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
185+
// imports
186+
exports.i(require(\\"-!../../../index.js??ref--4-0!./values.css\\"), \\"\\");
187+
exports.i(require(\\"-!../../../index.js??ref--4-0!./something.css\\"), \\"\\");
188+
189+
// module
190+
exports.push([module.id, \\"@import url(test-other.css) (min-width: 100px);\\\\n\\\\n._3r49KZIIAltPknAjdNVZ-7 {\\\\n color: \\" + require(\\"-!../../../index.js??ref--4-0!./values.css\\").locals[\\"def\\"] + \\";\\\\n}\\\\n\\\\n._4o0o5eKzoeDOSI0_cR8mr {\\\\n color: blue;\\\\n}\\\\n\\\\n._2wLXKM9pRjt1oRYvf0Wo3Q {\\\\n display: block;\\\\n}\\\\n\\\\n._1RBgqC8j3f4iU6k-ocmIG7 {\\\\n width: \\" + require(\\"-!../../../index.js??ref--4-0!./something.css\\").locals[\\"something\\"] + \\";\\\\n}\\\\n\\\\n._1lCIckG6C8tRZjGNDsAPWr {\\\\n color: \\" + require(\\"-!../../../index.js??ref--4-0!./values.css\\").locals[\\"other\\"] + \\";\\\\n}\\\\n\\\\n._1YL4f0i_603GTMRC_pnsP5 {\\\\n prop: \\" + require(\\"-!../../../index.js??ref--4-0!./values.css\\").locals[\\"def\\"] + \\";\\\\n duplicate: \\" + require(\\"-!../../../index.js??ref--4-0!./values.css\\").locals[\\"other\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
191+
192+
// exports
193+
exports.locals = {
194+
\\"def\\": \\"\\" + require(\\"-!../../../index.js??ref--4-0!./values.css\\").locals[\\"def\\"] + \\"\\",
195+
\\"other\\": \\"_2wLXKM9pRjt1oRYvf0Wo3Q\\",
196+
\\"something\\": \\"\\" + require(\\"-!../../../index.js??ref--4-0!./something.css\\").locals[\\"something\\"] + \\"\\",
197+
\\"foo\\": \\"_1YL4f0i_603GTMRC_pnsP5\\",
198+
\\"bar\\": \\"block\\",
199+
\\"ghi\\": \\"_3r49KZIIAltPknAjdNVZ-7\\",
200+
\\"class\\": \\"_4o0o5eKzoeDOSI0_cR8mr\\",
201+
\\"other-other\\": \\"_1RBgqC8j3f4iU6k-ocmIG7\\",
202+
\\"green\\": \\"_1lCIckG6C8tRZjGNDsAPWr\\"
203+
};"
204+
`;
205+
206+
exports[`import option false and modules \`local\`: warnings 1`] = `Array []`;
73207
74-
exports[`import option false and modules true: errors 1`] = `Array []`;
208+
exports[`import option false and modules \`true\`: errors 1`] = `Array []`;
75209
76-
exports[`import option false and modules true: module (evaluated) 1`] = `
210+
exports[`import option false and modules \`true\`: module (evaluated) 1`] = `
77211
Array [
78212
Array [
79213
2,
@@ -121,7 +255,7 @@ Array [
121255
]
122256
`;
123257
124-
exports[`import option false and modules true: module 1`] = `
258+
exports[`import option false and modules \`true\`: module 1`] = `
125259
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
126260
// imports
127261
exports.i(require(\\"-!../../../index.js??ref--4-0!./values.css\\"), \\"\\");
@@ -144,7 +278,7 @@ exports.locals = {
144278
};"
145279
`;
146280
147-
exports[`import option false and modules true: warnings 1`] = `Array []`;
281+
exports[`import option false and modules \`true\`: warnings 1`] = `Array []`;
148282
149283
exports[`import option false: errors 1`] = `Array []`;
150284

test/__snapshots__/loader.test.js.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -485,19 +485,19 @@ h1,h2,h3,h4,h5,h6 {
485485
}
486486
487487
main.hero, .hero.main {
488-
background-image: img1x.png;
488+
background-image: url(/webpack/public/path/img1x.png);
489489
}
490490
491491
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
492492
493493
main.hero, .hero.main {
494-
background-image: img2x.png;
494+
background-image: url(/webpack/public/path/img2x.png);
495495
}
496496
}
497497
498498
main.hero, .hero.main {
499499
background-image: -webkit-image-set(url(/webpack/public/path/img1x.png) 1x, url(/webpack/public/path/img2x.png) 2x);
500-
background-image: image-set(\\"img1x.png\\" 1x, \\"img2x.png\\" 2x);
500+
background-image: image-set(url(/webpack/public/path/img1x.png) 1x, url(/webpack/public/path/img2x.png) 2x);
501501
}
502502
503503
a {
@@ -520,7 +520,7 @@ exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
520520
521521
522522
// module
523-
exports.push([module.id, \\":root {\\\\n --fontSize: 1rem;\\\\n --mainColor: rgba(18,52,86,0.47059);\\\\n --secondaryColor: rgba(102, 51, 153, 0.9);\\\\n}\\\\n\\\\nhtml {\\\\n overflow-x: hidden;\\\\n overflow-y: auto;\\\\n overflow: hidden auto;\\\\n}\\\\n\\\\n@media (max-width: 50rem) {\\\\n body {\\\\n color: rgba(18,52,86,0.47059);\\\\n color: var(--mainColor);\\\\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\\\\n font-size: 1rem;\\\\n font-size: var(--fontSize);\\\\n line-height: calc(1rem * 1.5);\\\\n line-height: calc(var(--fontSize) * 1.5);\\\\n word-wrap: break-word;\\\\n padding-left: calc(1rem / 2 + 1px);\\\\n padding-right: calc(1rem / 2 + 1px);\\\\n padding-left: calc(var(--fontSize) / 2 + 1px);\\\\n padding-right: calc(var(--fontSize) / 2 + 1px);\\\\n }\\\\n}\\\\n\\\\nh1,h2,h3,h4,h5,h6 {\\\\n margin-top: 0;\\\\n margin-bottom: 0;\\\\n}\\\\n\\\\nmain.hero, .hero.main {\\\\n background-image: img1x.png;\\\\n}\\\\n\\\\n@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {\\\\n\\\\nmain.hero, .hero.main {\\\\n background-image: img2x.png;\\\\n}\\\\n}\\\\n\\\\nmain.hero, .hero.main {\\\\n background-image: -webkit-image-set(url(\\" + escape(require(\\"./img1x.png\\")) + \\") 1x, url(\\" + escape(require(\\"./img2x.png\\")) + \\") 2x);\\\\n background-image: image-set(\\\\\\"img1x.png\\\\\\" 1x, \\\\\\"img2x.png\\\\\\" 2x);\\\\n}\\\\n\\\\na {\\\\n color: rgba(0, 0, 255, 0.9)\\\\n}\\\\n\\\\na:hover {\\\\n color: #639;\\\\n }\\\\n\\", \\"\\"]);
523+
exports.push([module.id, \\":root {\\\\n --fontSize: 1rem;\\\\n --mainColor: rgba(18,52,86,0.47059);\\\\n --secondaryColor: rgba(102, 51, 153, 0.9);\\\\n}\\\\n\\\\nhtml {\\\\n overflow-x: hidden;\\\\n overflow-y: auto;\\\\n overflow: hidden auto;\\\\n}\\\\n\\\\n@media (max-width: 50rem) {\\\\n body {\\\\n color: rgba(18,52,86,0.47059);\\\\n color: var(--mainColor);\\\\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\\\\n font-size: 1rem;\\\\n font-size: var(--fontSize);\\\\n line-height: calc(1rem * 1.5);\\\\n line-height: calc(var(--fontSize) * 1.5);\\\\n word-wrap: break-word;\\\\n padding-left: calc(1rem / 2 + 1px);\\\\n padding-right: calc(1rem / 2 + 1px);\\\\n padding-left: calc(var(--fontSize) / 2 + 1px);\\\\n padding-right: calc(var(--fontSize) / 2 + 1px);\\\\n }\\\\n}\\\\n\\\\nh1,h2,h3,h4,h5,h6 {\\\\n margin-top: 0;\\\\n margin-bottom: 0;\\\\n}\\\\n\\\\nmain.hero, .hero.main {\\\\n background-image: url(\\" + escape(require(\\"./img1x.png\\")) + \\");\\\\n}\\\\n\\\\n@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {\\\\n\\\\nmain.hero, .hero.main {\\\\n background-image: url(\\" + escape(require(\\"./img2x.png\\")) + \\");\\\\n}\\\\n}\\\\n\\\\nmain.hero, .hero.main {\\\\n background-image: -webkit-image-set(url(\\" + escape(require(\\"./img1x.png\\")) + \\") 1x, url(\\" + escape(require(\\"./img2x.png\\")) + \\") 2x);\\\\n background-image: image-set(url(\\" + escape(require(\\"./img1x.png\\")) + \\") 1x, url(\\" + escape(require(\\"./img2x.png\\")) + \\") 2x);\\\\n}\\\\n\\\\na {\\\\n color: rgba(0, 0, 255, 0.9)\\\\n}\\\\n\\\\na:hover {\\\\n color: #639;\\\\n }\\\\n\\", \\"\\"]);
524524
525525
// exports
526526
"

0 commit comments

Comments
 (0)