From c673cf418e901c5050bc697eb45401dc9a42c477 Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Mon, 6 Apr 2020 17:36:13 +0300 Subject: [PATCH] feat: allow to determinate css modules using the `modules.auto` option (#1067) --- README.md | 60 +++++++++ src/index.js | 3 +- src/options.json | 10 ++ src/utils.js | 25 ++++ .../__snapshots__/modules-option.test.js.snap | 118 ++++++++++++++++++ .../validate-options.test.js.snap | 28 ++--- test/fixtures/modules/mode/modules.js | 5 + .../fixtures/modules/mode/relative.module.css | 3 + test/modules-option.test.js | 72 +++++++++++ 9 files changed, 309 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/modules/mode/modules.js create mode 100644 test/fixtures/modules/mode/relative.module.css diff --git a/README.md b/README.md index a6bee2f9..e62ee4a2 100644 --- a/README.md +++ b/README.md @@ -540,6 +540,66 @@ module.exports = { }; ``` +##### `auto` + +Type: `Boolean|RegExp` +Default: `'undefined'` + +Allows auto enable css modules based on filename. + +###### `Boolean` + +Possible values: + +- `true` - enable css modules for all files for which `/\.module\.\w+$/i.test(filename)` return true +- `false` - disable css modules + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + loader: 'css-loader', + options: { + modules: { + // All files for which /\.module\.\w+$/i.test(filename) return true + auto: true, + }, + }, + }, + ], + }, +}; +``` + +###### `RegExp` + +Enable css modules for files based on filename and satisfying `/youRegExp/.test(filename)` regex. + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + loader: 'css-loader', + options: { + modules: { + // All files for which /youRegExp/i.test(filename) return true + auto: /youRegExp/i, + }, + }, + }, + ], + }, +}; +``` + ##### `mode` Type: `String|Function` diff --git a/src/index.js b/src/index.js index c4e08cf6..2ee1b781 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,7 @@ import { getModuleCode, getModulesPlugins, normalizeSourceMap, + shouldUseModulesPlugins, } from './utils'; export default function loader(content, map, meta) { @@ -34,7 +35,7 @@ export default function loader(content, map, meta) { const sourceMap = options.sourceMap || false; const plugins = []; - if (options.modules) { + if (shouldUseModulesPlugins(options.modules, this.resourcePath)) { plugins.push(...getModulesPlugins(options, this)); } diff --git a/src/options.json b/src/options.json index 706e4e94..8d4e11f0 100644 --- a/src/options.json +++ b/src/options.json @@ -36,6 +36,16 @@ "type": "object", "additionalProperties": false, "properties": { + "auto": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "boolean" + } + ] + }, "mode": { "anyOf": [ { diff --git a/src/utils.js b/src/utils.js index a4c65119..d39ffcb8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -96,6 +96,30 @@ function getFilter(filter, resourcePath, defaultFilter = null) { }; } +function shouldUseModulesPlugins(modules, resourcePath) { + if (typeof modules === 'undefined') { + return false; + } + + if (typeof modules === 'boolean') { + return modules; + } + + if (typeof modules === 'string') { + return true; + } + + if (typeof modules.auto === 'boolean') { + return modules.auto ? /\.module\.\w+$/i.test(resourcePath) : false; + } + + if (modules.auto instanceof RegExp) { + return modules.auto.test(resourcePath); + } + + return true; +} + function getModulesPlugins(options, loaderContext) { let modulesOptions = { mode: 'local', @@ -405,4 +429,5 @@ export { getImportCode, getModuleCode, getExportCode, + shouldUseModulesPlugins, }; diff --git a/test/__snapshots__/modules-option.test.js.snap b/test/__snapshots__/modules-option.test.js.snap index ceab550c..216b4ead 100644 --- a/test/__snapshots__/modules-option.test.js.snap +++ b/test/__snapshots__/modules-option.test.js.snap @@ -3148,6 +3148,124 @@ Array [ exports[`"modules" option should work when the "getLocalIdent" option returns "false": warnings 1`] = `Array []`; +exports[`"modules" option should work with a modules.auto equal "false": errors 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto equal "false": module 1`] = ` +"// Imports +var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\"); +exports = ___CSS_LOADER_API_IMPORT___(false); +// Module +exports.push([module.id, \\".relative {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]); +// Exports +module.exports = exports; +" +`; + +exports[`"modules" option should work with a modules.auto equal "false": result 1`] = ` +Array [ + Array [ + "./modules/mode/relative.module.css", + ".relative { + color: red; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with a modules.auto equal "false": warnings 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto equal "true": errors 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto equal "true": module 1`] = ` +"// Imports +var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\"); +exports = ___CSS_LOADER_API_IMPORT___(false); +// Module +exports.push([module.id, \\".y35Nud52-ZFXmqL6AWueX {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]); +// Exports +exports.locals = { + \\"relative\\": \\"y35Nud52-ZFXmqL6AWueX\\" +}; +module.exports = exports; +" +`; + +exports[`"modules" option should work with a modules.auto equal "true": result 1`] = ` +Array [ + Array [ + "./modules/mode/relative.module.css", + ".y35Nud52-ZFXmqL6AWueX { + color: red; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with a modules.auto equal "true": warnings 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto returns "false": errors 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto returns "false": module 1`] = ` +"// Imports +var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\"); +exports = ___CSS_LOADER_API_IMPORT___(false); +// Module +exports.push([module.id, \\".relative {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]); +// Exports +module.exports = exports; +" +`; + +exports[`"modules" option should work with a modules.auto returns "false": result 1`] = ` +Array [ + Array [ + "./modules/mode/relative.module.css", + ".relative { + color: red; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with a modules.auto returns "false": warnings 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto returns "true": errors 1`] = `Array []`; + +exports[`"modules" option should work with a modules.auto returns "true": module 1`] = ` +"// Imports +var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\"); +exports = ___CSS_LOADER_API_IMPORT___(false); +// Module +exports.push([module.id, \\".y35Nud52-ZFXmqL6AWueX {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]); +// Exports +exports.locals = { + \\"relative\\": \\"y35Nud52-ZFXmqL6AWueX\\" +}; +module.exports = exports; +" +`; + +exports[`"modules" option should work with a modules.auto returns "true": result 1`] = ` +Array [ + Array [ + "./modules/mode/relative.module.css", + ".y35Nud52-ZFXmqL6AWueX { + color: red; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with a modules.auto returns "true": warnings 1`] = `Array []`; + exports[`"modules" option should work with case \`animation\` (\`modules\` value is \`false)\`: errors 1`] = `Array []`; exports[`"modules" option should work with case \`animation\` (\`modules\` value is \`false)\`: module 1`] = ` diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index bc1b2367..08024272 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -56,7 +56,7 @@ exports[`validate options should throw an error on the "modules" option with "{" exports[`validate options should throw an error on the "modules" option with "{"getLocalIdent":[]}" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } -> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules). Details: * options.modules.getLocalIdent should be one of these: @@ -79,7 +79,7 @@ exports[`validate options should throw an error on the "modules" option with "{" exports[`validate options should throw an error on the "modules" option with "{"localIdentRegExp":true}" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } -> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules). Details: * options.modules.localIdentRegExp should be one of these: @@ -92,7 +92,7 @@ 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 should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, 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: @@ -106,7 +106,7 @@ 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":"locals"}" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, 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: @@ -120,7 +120,7 @@ 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":"pures"}" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, 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: @@ -134,7 +134,7 @@ 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":true}" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, 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: @@ -148,53 +148,53 @@ exports[`validate options should throw an error on the "modules" option with "{" exports[`validate options should throw an error on the "modules" option with "globals" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } -> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules). Details: * options.modules should be a boolean. * options.modules should be one of these: \\"local\\" | \\"global\\" | \\"pure\\" * options.modules should be an object: - object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" + object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" `; exports[`validate options should throw an error on the "modules" option with "locals" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } -> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules). Details: * options.modules should be a boolean. * options.modules should be one of these: \\"local\\" | \\"global\\" | \\"pure\\" * options.modules should be an object: - object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" + object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" `; exports[`validate options should throw an error on the "modules" option with "pures" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } -> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules). Details: * options.modules should be a boolean. * options.modules should be one of these: \\"local\\" | \\"global\\" | \\"pure\\" * options.modules should be an object: - object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" + object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" `; exports[`validate options should throw an error on the "modules" option with "true" value 1`] = ` "Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema. - options.modules should be one of these: - boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } + boolean | \\"local\\" | \\"global\\" | \\"pure\\" | object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? } -> Enables/Disables CSS Modules and their configuration (https://github.com/webpack-contrib/css-loader#modules). Details: * options.modules should be a boolean. * options.modules should be one of these: \\"local\\" | \\"global\\" | \\"pure\\" * options.modules should be an object: - object { mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" + object { auto?, mode?, exportGlobals?, localIdentName?, localIdentRegExp?, context?, hashPrefix?, getLocalIdent? }" `; exports[`validate options should throw an error on the "onlyLocals" option with "true" value 1`] = ` diff --git a/test/fixtures/modules/mode/modules.js b/test/fixtures/modules/mode/modules.js new file mode 100644 index 00000000..27eb52af --- /dev/null +++ b/test/fixtures/modules/mode/modules.js @@ -0,0 +1,5 @@ +import relative from './relative.module.css'; + +__export__ = relative; + +export default relative; diff --git a/test/fixtures/modules/mode/relative.module.css b/test/fixtures/modules/mode/relative.module.css new file mode 100644 index 00000000..73597627 --- /dev/null +++ b/test/fixtures/modules/mode/relative.module.css @@ -0,0 +1,3 @@ +.relative { + color: red; +} diff --git a/test/modules-option.test.js b/test/modules-option.test.js index 31762f64..23617a33 100644 --- a/test/modules-option.test.js +++ b/test/modules-option.test.js @@ -688,4 +688,76 @@ describe('"modules" option', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should work with a modules.auto equal "false"', async () => { + const compiler = getCompiler('./modules/mode/modules.js', { + modules: { + auto: false, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/mode/relative.module.css', stats) + ).toMatchSnapshot('module'); + expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should work with a modules.auto equal "true"', async () => { + const compiler = getCompiler('./modules/mode/modules.js', { + modules: { + auto: true, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/mode/relative.module.css', stats) + ).toMatchSnapshot('module'); + expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should work with a modules.auto returns "true"', async () => { + const compiler = getCompiler('./modules/mode/modules.js', { + modules: { + auto: /relative.module.css$/, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/mode/relative.module.css', stats) + ).toMatchSnapshot('module'); + expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should work with a modules.auto returns "false"', async () => { + const compiler = getCompiler('./modules/mode/modules.js', { + modules: { + auto: /will no pass/, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/mode/relative.module.css', stats) + ).toMatchSnapshot('module'); + expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); });