Skip to content

Commit

Permalink
feat: allow to disable css modules and disable their by default (#842)
Browse files Browse the repository at this point in the history
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'`
  • Loading branch information
evilebottnawi authored Dec 3, 2018
1 parent ee2d253 commit 889dc7f
Show file tree
Hide file tree
Showing 12 changed files with 2,905 additions and 438 deletions.
34 changes: 25 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ It's useful when you, for instance, need to post process the CSS as a string.
|:--:|:--:|:-----:|:----------|
|**[`url`](#url)**|`{Boolean}`|`true`| Enable/Disable `url()` handling|
|**[`import`](#import)** |`{Boolean}`|`true`| Enable/Disable @import handling|
|**[`modules`](#modules)**|`{Boolean}`|`false`|Enable/Disable CSS Modules|
|**[`modules`](#modules)**|`{Boolean\|String}`|`false`|Enable/Disable CSS Modules and setup mode|
|**[`localIdentName`](#localidentname)**|`{String}`|`[hash:base64]`|Configure the generated ident|
|**[`sourceMap`](#sourcemap)**|`{Boolean}`|`false`|Enable/Disable Sourcemaps|
|**[`camelCase`](#camelcase)**|`{Boolean\|String}`|`false`|Export Classnames in CamelCase|
Expand Down Expand Up @@ -129,17 +129,33 @@ To import styles from a node module path, prefix it with a `~`:

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

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

This enables local scoped CSS by default. (You can switch it off with `:global(...)` or `:global` for selectors and/or rules.).
|Name|Type|Description|
|:--:|:--:|:----------|
|**`true`**|`{Boolean}`|Enables local scoped CSS by default (use **local** mode by default)|
|**`false`**|`{Boolean}`|Disable the **CSS Modules** spec, all **CSS Modules** features (like `@value`, `:local`, `:global` and `composes`) will not work|
|**`'local'`** |`{String}`|Enables local scoped CSS by default (same as `true` value)|
|**`'global'`**|`{String}`|Enables global scoped CSS by default|

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.

You can read about **modes** below

#### `Scope`
##### `Scope`

Using `local` value requires you to specify `:global` classes.
Using `global` value requires you to specify `:local` classes.

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

Styles can be locally scoped to avoid globally scoping styles.

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

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.
With `:local` (without brackets) local mode can be switched on for this selector.
The `:global(.className)` nocation can be used to declare an explicit global selector.
With `:global` (without brackets) global mode can be switched on for this selector.

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

Expand Down Expand Up @@ -177,7 +193,7 @@ file.png => ./file.png

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

#### `Composing`
##### `Composing`

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

Expand Down Expand Up @@ -213,7 +229,7 @@ exports.locals = {
}
```

#### `Importing`
##### `Importing`

To import a local classname from another module.

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

To include source maps set the `sourceMap` option.

I.e. the extract-text-webpack-plugin can handle them.
I.e. the `mini-css-extract-plugin` can handle them.

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.

Expand Down
80 changes: 46 additions & 34 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,43 +62,55 @@ module.exports = function loader(content, map, meta) {

const resolveImport = options.import !== false;
const resolveUrl = options.url !== false;
const loaderContext = this;

const plugins = [
modulesValues,
localByDefault({
mode: options.modules ? 'local' : 'global',
rewriteUrl(global, url) {
if (resolveUrl) {
// eslint-disable-next-line no-param-reassign
url = url.trim();

if (!url.replace(/\s/g, '').length || !isUrlRequest(url)) {
return url;
}

if (global) {
return urlToRequest(url);
const plugins = [];

if (options.modules) {
const loaderContext = this;
const mode =
typeof options.modules === 'boolean' ? 'local' : options.modules;

plugins.push(
modulesValues,
localByDefault({
mode,
rewriteUrl(global, url) {
if (resolveUrl) {
// eslint-disable-next-line no-param-reassign
url = url.trim();

if (!url.replace(/\s/g, '').length || !isUrlRequest(url)) {
return url;
}

if (global) {
return urlToRequest(url);
}
}
}

return url;
},
}),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName(exportName) {
const localIdentName = options.localIdentName || '[hash:base64]';
const customGetLocalIdent = options.getLocalIdent || getLocalIdent;

return customGetLocalIdent(loaderContext, localIdentName, exportName, {
regExp: options.localIdentRegExp,
hashPrefix: options.hashPrefix || '',
context: options.context,
});
},
}),
];
return url;
},
}),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName(exportName) {
const localIdentName = options.localIdentName || '[hash:base64]';
const customGetLocalIdent = options.getLocalIdent || getLocalIdent;

return customGetLocalIdent(
loaderContext,
localIdentName,
exportName,
{
regExp: options.localIdentRegExp,
hashPrefix: options.hashPrefix || '',
context: options.context,
}
);
},
})
);
}

if (resolveImport) {
plugins.push(importParser());
Expand Down
150 changes: 142 additions & 8 deletions test/__snapshots__/import-option.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,8 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

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

exports[`import option false and modules false: module (evaluated) 1`] = `
exports[`import option false and modules \`false\`: module (evaluated) 1`] = `
Array [
Array [
1,
"@import url(test-other.css) (min-width: 100px);
@value def from './values.css';
@value other from './values.css';
@value other from './values.css';
@value something from './something.css';
@value foo: blue;
@value bar: block;
.ghi {
color: def;
}
.class {
color: foo;
}
.other {
display: bar;
}
.other-other {
width: something;
}
.green {
color: other;
}
.foo {
prop: def;
duplicate: other;
}
",
"",
],
]
`;

exports[`import option false and modules \`false\`: module 1`] = `
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
// imports
// module
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\\", \\"\\"]);
// exports
"
`;

exports[`import option false and modules \`false\`: warnings 1`] = `Array []`;

exports[`import option false and modules \`global\`: errors 1`] = `Array []`;

exports[`import option false and modules \`global\`: module (evaluated) 1`] = `
Array [
Array [
2,
Expand Down Expand Up @@ -50,7 +109,7 @@ Array [
]
`;

exports[`import option false and modules false: module 1`] = `
exports[`import option false and modules \`global\`: module 1`] = `
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
// imports
exports.i(require(\\"-!../../../index.js??ref--4-0!./values.css\\"), \\"\\");
Expand All @@ -69,11 +128,86 @@ exports.locals = {
};"
`;
exports[`import option false and modules false: warnings 1`] = `Array []`;
exports[`import option false and modules \`global\`: warnings 1`] = `Array []`;
exports[`import option false and modules \`local\`: errors 1`] = `Array []`;
exports[`import option false and modules \`local\`: module (evaluated) 1`] = `
Array [
Array [
2,
"
",
"",
],
Array [
3,
"
",
"",
],
Array [
1,
"@import url(test-other.css) (min-width: 100px);
._3r49KZIIAltPknAjdNVZ-7 {
color: red;
}
._4o0o5eKzoeDOSI0_cR8mr {
color: blue;
}
._2wLXKM9pRjt1oRYvf0Wo3Q {
display: block;
}
._1RBgqC8j3f4iU6k-ocmIG7 {
width: 2112moon;
}
._1lCIckG6C8tRZjGNDsAPWr {
color: green;
}
._1YL4f0i_603GTMRC_pnsP5 {
prop: red;
duplicate: green;
}
",
"",
],
]
`;
exports[`import option false and modules \`local\`: module 1`] = `
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
// imports
exports.i(require(\\"-!../../../index.js??ref--4-0!./values.css\\"), \\"\\");
exports.i(require(\\"-!../../../index.js??ref--4-0!./something.css\\"), \\"\\");
// module
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\\", \\"\\"]);
// exports
exports.locals = {
\\"def\\": \\"\\" + require(\\"-!../../../index.js??ref--4-0!./values.css\\").locals[\\"def\\"] + \\"\\",
\\"other\\": \\"_2wLXKM9pRjt1oRYvf0Wo3Q\\",
\\"something\\": \\"\\" + require(\\"-!../../../index.js??ref--4-0!./something.css\\").locals[\\"something\\"] + \\"\\",
\\"foo\\": \\"_1YL4f0i_603GTMRC_pnsP5\\",
\\"bar\\": \\"block\\",
\\"ghi\\": \\"_3r49KZIIAltPknAjdNVZ-7\\",
\\"class\\": \\"_4o0o5eKzoeDOSI0_cR8mr\\",
\\"other-other\\": \\"_1RBgqC8j3f4iU6k-ocmIG7\\",
\\"green\\": \\"_1lCIckG6C8tRZjGNDsAPWr\\"
};"
`;
exports[`import option false and modules \`local\`: warnings 1`] = `Array []`;
exports[`import option false and modules true: errors 1`] = `Array []`;
exports[`import option false and modules \`true\`: errors 1`] = `Array []`;
exports[`import option false and modules true: module (evaluated) 1`] = `
exports[`import option false and modules \`true\`: module (evaluated) 1`] = `
Array [
Array [
2,
Expand Down Expand Up @@ -121,7 +255,7 @@ Array [
]
`;
exports[`import option false and modules true: module 1`] = `
exports[`import option false and modules \`true\`: module 1`] = `
"exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
// imports
exports.i(require(\\"-!../../../index.js??ref--4-0!./values.css\\"), \\"\\");
Expand All @@ -144,7 +278,7 @@ exports.locals = {
};"
`;
exports[`import option false and modules true: warnings 1`] = `Array []`;
exports[`import option false and modules \`true\`: warnings 1`] = `Array []`;
exports[`import option false: errors 1`] = `Array []`;
Expand Down
8 changes: 4 additions & 4 deletions test/__snapshots__/loader.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -485,19 +485,19 @@ h1,h2,h3,h4,h5,h6 {
}
main.hero, .hero.main {
background-image: img1x.png;
background-image: url(/webpack/public/path/img1x.png);
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
main.hero, .hero.main {
background-image: img2x.png;
background-image: url(/webpack/public/path/img2x.png);
}
}
main.hero, .hero.main {
background-image: -webkit-image-set(url(/webpack/public/path/img1x.png) 1x, url(/webpack/public/path/img2x.png) 2x);
background-image: image-set(\\"img1x.png\\" 1x, \\"img2x.png\\" 2x);
background-image: image-set(url(/webpack/public/path/img1x.png) 1x, url(/webpack/public/path/img2x.png) 2x);
}
a {
Expand All @@ -520,7 +520,7 @@ exports = module.exports = require(\\"../../../lib/runtime/api.js\\")(false);
// module
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\\", \\"\\"]);
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\\", \\"\\"]);
// exports
"
Expand Down
Loading

0 comments on commit 889dc7f

Please sign in to comment.