Skip to content

Commit 0d8ac3b

Browse files
feat: the modules.mode option may be a function (#1065)
1 parent 431f620 commit 0d8ac3b

10 files changed

+299
-33
lines changed

README.md

+40-1
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,17 @@ module.exports = {
541541

542542
##### `mode`
543543

544-
Type: `String`
544+
Type: `String|Function`
545545
Default: `'local'`
546546

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

549+
###### `String`
550+
551+
Possible values:
552+
553+
`local`, `global`, `pure`
554+
549555
**webpack.config.js**
550556

551557
```js
@@ -566,6 +572,39 @@ module.exports = {
566572
};
567573
```
568574

575+
###### `Function`
576+
577+
Allows set different values for the `mode` option based on a filename
578+
579+
Possible return values - `local`, `global` and `pure`
580+
581+
**webpack.config.js**
582+
583+
```js
584+
module.exports = {
585+
module: {
586+
rules: [
587+
{
588+
test: /\.css$/i,
589+
loader: 'css-loader',
590+
options: {
591+
modules: {
592+
// Callback must return "local", "global" or "pure"
593+
mode: (filename) => {
594+
if (/global.css$/i.test(filename)) {
595+
return 'global';
596+
}
597+
598+
return 'local';
599+
},
600+
},
601+
},
602+
},
603+
],
604+
},
605+
};
606+
```
607+
569608
##### `localIdentName`
570609

571610
Type: `String`

src/options.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,14 @@
3737
"additionalProperties": false,
3838
"properties": {
3939
"mode": {
40-
"enum": ["local", "global", "pure"]
40+
"anyOf": [
41+
{
42+
"enum": ["local", "global", "pure"]
43+
},
44+
{
45+
"instanceof": "Function"
46+
}
47+
]
4148
},
4249
"localIdentName": {
4350
"type": "string"

src/utils.js

+39-23
Original file line numberDiff line numberDiff line change
@@ -115,25 +115,24 @@ function getModulesPlugins(options, loaderContext) {
115115
modulesOptions = Object.assign({}, modulesOptions, options.modules);
116116
}
117117

118-
return [
119-
modulesValues,
120-
localByDefault({ mode: modulesOptions.mode }),
121-
extractImports(),
122-
modulesScope({
123-
generateScopedName: function generateScopedName(exportName) {
124-
let localIdent = modulesOptions.getLocalIdent(
125-
loaderContext,
126-
modulesOptions.localIdentName,
127-
exportName,
128-
{
129-
context: modulesOptions.context,
130-
hashPrefix: modulesOptions.hashPrefix,
131-
regExp: modulesOptions.localIdentRegExp,
132-
}
133-
);
118+
if (typeof modulesOptions.mode === 'function') {
119+
const modeFromFunction = modulesOptions.mode(loaderContext.resourcePath);
120+
121+
if (modeFromFunction === 'local' || modeFromFunction === 'global') {
122+
modulesOptions.mode = modeFromFunction;
123+
}
124+
}
125+
126+
let plugins = [];
134127

135-
if (!localIdent) {
136-
localIdent = getLocalIdent(
128+
try {
129+
plugins = [
130+
modulesValues,
131+
localByDefault({ mode: modulesOptions.mode }),
132+
extractImports(),
133+
modulesScope({
134+
generateScopedName: function generateScopedName(exportName) {
135+
let localIdent = modulesOptions.getLocalIdent(
137136
loaderContext,
138137
modulesOptions.localIdentName,
139138
exportName,
@@ -143,12 +142,29 @@ function getModulesPlugins(options, loaderContext) {
143142
regExp: modulesOptions.localIdentRegExp,
144143
}
145144
);
146-
}
147145

148-
return localIdent;
149-
},
150-
}),
151-
];
146+
if (!localIdent) {
147+
localIdent = getLocalIdent(
148+
loaderContext,
149+
modulesOptions.localIdentName,
150+
exportName,
151+
{
152+
context: modulesOptions.context,
153+
hashPrefix: modulesOptions.hashPrefix,
154+
regExp: modulesOptions.localIdentRegExp,
155+
}
156+
);
157+
}
158+
159+
return localIdent;
160+
},
161+
}),
162+
];
163+
} catch (error) {
164+
loaderContext.emitError(error);
165+
}
166+
167+
return plugins;
152168
}
153169

154170
function normalizeSourceMap(map) {

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

+101
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,107 @@ div:not(.\\\\1F600) {
363363
364364
exports[`"modules" option issue #995: warnings 1`] = `Array []`;
365365
366+
exports[`"modules" option issue #1063 throw error: errors 1`] = `
367+
Array [
368+
"ModuleError: Module Error (from \`replaced original path\`):
369+
options.mode must be either \\"global\\", \\"local\\" or \\"pure\\" (default \\"local\\")",
370+
"ModuleError: Module Error (from \`replaced original path\`):
371+
options.mode must be either \\"global\\", \\"local\\" or \\"pure\\" (default \\"local\\")",
372+
]
373+
`;
374+
375+
exports[`"modules" option issue #1063 throw error: module 1`] = `
376+
"// Imports
377+
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
378+
exports = ___CSS_LOADER_API_IMPORT___(false);
379+
// Module
380+
exports.push([module.id, \\".classNameLocalFile {\\\\n color: green;\\\\n}\\\\n\\\\n:global(.otherClassLocalFile) {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]);
381+
// Exports
382+
module.exports = exports;
383+
"
384+
`;
385+
386+
exports[`"modules" option issue #1063 throw error: module 2`] = `
387+
"// Imports
388+
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
389+
exports = ___CSS_LOADER_API_IMPORT___(false);
390+
// Module
391+
exports.push([module.id, \\".classNameGlobalFile {\\\\n color: black;\\\\n}\\\\n\\\\n:local(.otherClassGlobalFile) {\\\\n color: coral;\\\\n}\\\\n\\", \\"\\"]);
392+
// Exports
393+
module.exports = exports;
394+
"
395+
`;
396+
397+
exports[`"modules" option issue #1063 throw error: result 1`] = `
398+
".classNameLocalFile {
399+
color: green;
400+
}
401+
402+
:global(.otherClassLocalFile) {
403+
color: blue;
404+
}
405+
.classNameGlobalFile {
406+
color: black;
407+
}
408+
409+
:local(.otherClassGlobalFile) {
410+
color: coral;
411+
}
412+
"
413+
`;
414+
415+
exports[`"modules" option issue #1063 throw error: warnings 1`] = `Array []`;
416+
417+
exports[`"modules" option issue #1063: errors 1`] = `Array []`;
418+
419+
exports[`"modules" option issue #1063: module 1`] = `
420+
"// Imports
421+
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
422+
exports = ___CSS_LOADER_API_IMPORT___(false);
423+
// Module
424+
exports.push([module.id, \\"._2cZDGA9F7Kx1vfQmWcLP51 {\\\\n color: green;\\\\n}\\\\n\\\\n.otherClassLocalFile {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]);
425+
// Exports
426+
exports.locals = {
427+
\\"classNameLocalFile\\": \\"_2cZDGA9F7Kx1vfQmWcLP51\\"
428+
};
429+
module.exports = exports;
430+
"
431+
`;
432+
433+
exports[`"modules" option issue #1063: module 2`] = `
434+
"// Imports
435+
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
436+
exports = ___CSS_LOADER_API_IMPORT___(false);
437+
// Module
438+
exports.push([module.id, \\".classNameGlobalFile {\\\\n color: black;\\\\n}\\\\n\\\\n._2rcag09JpwrP4_hfyyRmm- {\\\\n color: coral;\\\\n}\\\\n\\", \\"\\"]);
439+
// Exports
440+
exports.locals = {
441+
\\"otherClassGlobalFile\\": \\"_2rcag09JpwrP4_hfyyRmm-\\"
442+
};
443+
module.exports = exports;
444+
"
445+
`;
446+
447+
exports[`"modules" option issue #1063: result 1`] = `
448+
"._2cZDGA9F7Kx1vfQmWcLP51 {
449+
color: green;
450+
}
451+
452+
.otherClassLocalFile {
453+
color: blue;
454+
}
455+
.classNameGlobalFile {
456+
color: black;
457+
}
458+
459+
._2rcag09JpwrP4_hfyyRmm- {
460+
color: coral;
461+
}
462+
"
463+
`;
464+
465+
exports[`"modules" option issue #1063: warnings 1`] = `Array []`;
466+
366467
exports[`"modules" option should avoid unnecessary "require": errors 1`] = `Array []`;
367468
368469
exports[`"modules" option should avoid unnecessary "require": module 1`] = `

test/__snapshots__/validate-options.test.js.snap

+40-8
Original file line numberDiff line numberDiff line change
@@ -86,26 +86,58 @@ exports[`validate options should throw an error on the "modules" option with "{"
8686
8787
exports[`validate options should throw an error on the "modules" option with "{"mode":"globals"}" value 1`] = `
8888
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
89-
- options.modules.mode should be one of these:
90-
\\"local\\" | \\"global\\" | \\"pure\\""
89+
- options.modules should be one of these:
90+
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
91+
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
92+
Details:
93+
* options.modules.mode should be one of these:
94+
\\"local\\" | \\"global\\" | \\"pure\\" | function
95+
Details:
96+
* options.modules.mode should be one of these:
97+
\\"local\\" | \\"global\\" | \\"pure\\"
98+
* options.modules.mode should be an instance of function."
9199
`;
92100
93101
exports[`validate options should throw an error on the "modules" option with "{"mode":"locals"}" value 1`] = `
94102
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
95-
- options.modules.mode should be one of these:
96-
\\"local\\" | \\"global\\" | \\"pure\\""
103+
- options.modules should be one of these:
104+
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
105+
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
106+
Details:
107+
* options.modules.mode should be one of these:
108+
\\"local\\" | \\"global\\" | \\"pure\\" | function
109+
Details:
110+
* options.modules.mode should be one of these:
111+
\\"local\\" | \\"global\\" | \\"pure\\"
112+
* options.modules.mode should be an instance of function."
97113
`;
98114
99115
exports[`validate options should throw an error on the "modules" option with "{"mode":"pures"}" value 1`] = `
100116
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
101-
- options.modules.mode should be one of these:
102-
\\"local\\" | \\"global\\" | \\"pure\\""
117+
- options.modules should be one of these:
118+
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
119+
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
120+
Details:
121+
* options.modules.mode should be one of these:
122+
\\"local\\" | \\"global\\" | \\"pure\\" | function
123+
Details:
124+
* options.modules.mode should be one of these:
125+
\\"local\\" | \\"global\\" | \\"pure\\"
126+
* options.modules.mode should be an instance of function."
103127
`;
104128
105129
exports[`validate options should throw an error on the "modules" option with "{"mode":true}" value 1`] = `
106130
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
107-
- options.modules.mode should be one of these:
108-
\\"local\\" | \\"global\\" | \\"pure\\""
131+
- options.modules should be one of these:
132+
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }
133+
-> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules).
134+
Details:
135+
* options.modules.mode should be one of these:
136+
\\"local\\" | \\"global\\" | \\"pure\\" | function
137+
Details:
138+
* options.modules.mode should be one of these:
139+
\\"local\\" | \\"global\\" | \\"pure\\"
140+
* options.modules.mode should be an instance of function."
109141
`;
110142
111143
exports[`validate options should throw an error on the "modules" option with "globals" value 1`] = `
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.classNameGlobalFile {
2+
color: black;
3+
}
4+
5+
:local(.otherClassGlobalFile) {
6+
color: coral;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import css1 from './local.css';
2+
import css2 from './global.css';
3+
4+
__export__ = css1 + css2;
5+
6+
export default css1 + css2;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.classNameLocalFile {
2+
color: green;
3+
}
4+
5+
:global(.otherClassLocalFile) {
6+
color: blue;
7+
}

0 commit comments

Comments
 (0)