Skip to content

Commit

Permalink
feat: the modules.mode option may be a function (#1065)
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Apr 6, 2020
1 parent 431f620 commit 0d8ac3b
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 33 deletions.
41 changes: 40 additions & 1 deletion README.md
Expand Up @@ -541,11 +541,17 @@ module.exports = {

##### `mode`

Type: `String`
Type: `String|Function`
Default: `'local'`

Setup `mode` option. You can omit the value when you want `local` mode.

###### `String`

Possible values:

`local`, `global`, `pure`

**webpack.config.js**

```js
Expand All @@ -566,6 +572,39 @@ module.exports = {
};
```

###### `Function`

Allows set different values for the `mode` option based on a filename

Possible return values - `local`, `global` and `pure`

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: 'css-loader',
options: {
modules: {
// Callback must return "local", "global" or "pure"
mode: (filename) => {
if (/global.css$/i.test(filename)) {
return 'global';
}

return 'local';
},
},
},
},
],
},
};
```

##### `localIdentName`

Type: `String`
Expand Down
9 changes: 8 additions & 1 deletion src/options.json
Expand Up @@ -37,7 +37,14 @@
"additionalProperties": false,
"properties": {
"mode": {
"enum": ["local", "global", "pure"]
"anyOf": [
{
"enum": ["local", "global", "pure"]
},
{
"instanceof": "Function"
}
]
},
"localIdentName": {
"type": "string"
Expand Down
62 changes: 39 additions & 23 deletions src/utils.js
Expand Up @@ -115,25 +115,24 @@ function getModulesPlugins(options, loaderContext) {
modulesOptions = Object.assign({}, modulesOptions, options.modules);
}

return [
modulesValues,
localByDefault({ mode: modulesOptions.mode }),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName(exportName) {
let localIdent = modulesOptions.getLocalIdent(
loaderContext,
modulesOptions.localIdentName,
exportName,
{
context: modulesOptions.context,
hashPrefix: modulesOptions.hashPrefix,
regExp: modulesOptions.localIdentRegExp,
}
);
if (typeof modulesOptions.mode === 'function') {
const modeFromFunction = modulesOptions.mode(loaderContext.resourcePath);

if (modeFromFunction === 'local' || modeFromFunction === 'global') {
modulesOptions.mode = modeFromFunction;
}
}

let plugins = [];

if (!localIdent) {
localIdent = getLocalIdent(
try {
plugins = [
modulesValues,
localByDefault({ mode: modulesOptions.mode }),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName(exportName) {
let localIdent = modulesOptions.getLocalIdent(
loaderContext,
modulesOptions.localIdentName,
exportName,
Expand All @@ -143,12 +142,29 @@ function getModulesPlugins(options, loaderContext) {
regExp: modulesOptions.localIdentRegExp,
}
);
}

return localIdent;
},
}),
];
if (!localIdent) {
localIdent = getLocalIdent(
loaderContext,
modulesOptions.localIdentName,
exportName,
{
context: modulesOptions.context,
hashPrefix: modulesOptions.hashPrefix,
regExp: modulesOptions.localIdentRegExp,
}
);
}

return localIdent;
},
}),
];
} catch (error) {
loaderContext.emitError(error);
}

return plugins;
}

function normalizeSourceMap(map) {
Expand Down
101 changes: 101 additions & 0 deletions test/__snapshots__/modules-option.test.js.snap
Expand Up @@ -363,6 +363,107 @@ div:not(.\\\\1F600) {
exports[`"modules" option issue #995: warnings 1`] = `Array []`;
exports[`"modules" option issue #1063 throw error: errors 1`] = `
Array [
"ModuleError: Module Error (from \`replaced original path\`):
options.mode must be either \\"global\\", \\"local\\" or \\"pure\\" (default \\"local\\")",
"ModuleError: Module Error (from \`replaced original path\`):
options.mode must be either \\"global\\", \\"local\\" or \\"pure\\" (default \\"local\\")",
]
`;
exports[`"modules" option issue #1063 throw error: module 1`] = `
"// Imports
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.id, \\".classNameLocalFile {\\\\n color: green;\\\\n}\\\\n\\\\n:global(.otherClassLocalFile) {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]);
// Exports
module.exports = exports;
"
`;
exports[`"modules" option issue #1063 throw error: module 2`] = `
"// Imports
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.id, \\".classNameGlobalFile {\\\\n color: black;\\\\n}\\\\n\\\\n:local(.otherClassGlobalFile) {\\\\n color: coral;\\\\n}\\\\n\\", \\"\\"]);
// Exports
module.exports = exports;
"
`;
exports[`"modules" option issue #1063 throw error: result 1`] = `
".classNameLocalFile {
color: green;
}
:global(.otherClassLocalFile) {
color: blue;
}
.classNameGlobalFile {
color: black;
}
:local(.otherClassGlobalFile) {
color: coral;
}
"
`;
exports[`"modules" option issue #1063 throw error: warnings 1`] = `Array []`;
exports[`"modules" option issue #1063: errors 1`] = `Array []`;
exports[`"modules" option issue #1063: module 1`] = `
"// Imports
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.id, \\"._2cZDGA9F7Kx1vfQmWcLP51 {\\\\n color: green;\\\\n}\\\\n\\\\n.otherClassLocalFile {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]);
// Exports
exports.locals = {
\\"classNameLocalFile\\": \\"_2cZDGA9F7Kx1vfQmWcLP51\\"
};
module.exports = exports;
"
`;
exports[`"modules" option issue #1063: module 2`] = `
"// Imports
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.id, \\".classNameGlobalFile {\\\\n color: black;\\\\n}\\\\n\\\\n._2rcag09JpwrP4_hfyyRmm- {\\\\n color: coral;\\\\n}\\\\n\\", \\"\\"]);
// Exports
exports.locals = {
\\"otherClassGlobalFile\\": \\"_2rcag09JpwrP4_hfyyRmm-\\"
};
module.exports = exports;
"
`;
exports[`"modules" option issue #1063: result 1`] = `
"._2cZDGA9F7Kx1vfQmWcLP51 {
color: green;
}
.otherClassLocalFile {
color: blue;
}
.classNameGlobalFile {
color: black;
}
._2rcag09JpwrP4_hfyyRmm- {
color: coral;
}
"
`;
exports[`"modules" option issue #1063: warnings 1`] = `Array []`;
exports[`"modules" option should avoid unnecessary "require": errors 1`] = `Array []`;
exports[`"modules" option should avoid unnecessary "require": module 1`] = `
Expand Down
48 changes: 40 additions & 8 deletions test/__snapshots__/validate-options.test.js.snap
Expand Up @@ -86,26 +86,58 @@ exports[`validate options should throw an error on the "modules" option with "{"
exports[`validate options should throw an error on the "modules" option with "{"mode":"globals"}" value 1`] = `
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\""
- options.modules should be one of these:
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\" | function
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\"
* options.modules.mode should be an instance of function."
`;
exports[`validate options should throw an error on the "modules" option with "{"mode":"locals"}" value 1`] = `
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\""
- options.modules should be one of these:
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\" | function
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\"
* options.modules.mode should be an instance of function."
`;
exports[`validate options should throw an error on the "modules" option with "{"mode":"pures"}" value 1`] = `
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\""
- options.modules should be one of these:
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\" | function
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\"
* options.modules.mode should be an instance of function."
`;
exports[`validate options should throw an error on the "modules" option with "{"mode":true}" value 1`] = `
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\""
- options.modules should be one of these:
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\" | function
Details:
* options.modules.mode should be one of these:
\\"local\\" | \\"global\\" | \\"pure\\"
* options.modules.mode should be an instance of function."
`;
exports[`validate options should throw an error on the "modules" option with "globals" value 1`] = `
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/modules/issue-1063/global.css
@@ -0,0 +1,7 @@
.classNameGlobalFile {
color: black;
}

:local(.otherClassGlobalFile) {
color: coral;
}
6 changes: 6 additions & 0 deletions test/fixtures/modules/issue-1063/issue-1063.js
@@ -0,0 +1,6 @@
import css1 from './local.css';
import css2 from './global.css';

__export__ = css1 + css2;

export default css1 + css2;
7 changes: 7 additions & 0 deletions test/fixtures/modules/issue-1063/local.css
@@ -0,0 +1,7 @@
.classNameLocalFile {
color: green;
}

:global(.otherClassLocalFile) {
color: blue;
}

0 comments on commit 0d8ac3b

Please sign in to comment.