From 15889db9a2d5d51712fc12e03b12a60c22fc33f4 Mon Sep 17 00:00:00 2001 From: Alexander Krasnoyarov Date: Sat, 3 Oct 2020 18:34:59 +0300 Subject: [PATCH] feat: added `modules.namedExport` (#485) --- README.md | 90 ++++++++++++++-- src/index.js | 57 +++++++--- src/options.json | 10 ++ src/runtime/isEqualLocals.js | 12 ++- .../__snapshots__/modules-option.test.js.snap | 93 ++++++++++++++++ .../validate-options.test.js.snap | 28 +++-- test/fixtures/named-export.css | 8 ++ test/fixtures/named-export.js | 17 +++ test/manual/src/index.js | 100 ++++++++++++++++++ .../src/style.named-export.lazy.module.css | 16 +++ test/manual/src/style.named-export.module.css | 16 +++ test/manual/webpack.config.js | 60 ++++++++++- test/modules-option.test.js | 64 +++++++++++ test/runtime/isEqualLocals.test.js | 17 +++ test/validate-options.test.js | 4 + 15 files changed, 556 insertions(+), 36 deletions(-) create mode 100644 test/__snapshots__/modules-option.test.js.snap create mode 100644 test/fixtures/named-export.css create mode 100644 test/fixtures/named-export.js create mode 100644 test/manual/src/style.named-export.lazy.module.css create mode 100644 test/manual/src/style.named-export.module.css create mode 100644 test/modules-option.test.js diff --git a/README.md b/README.md index a9551a25..e8968124 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,14 @@ module.exports = { ## Options -| Name | Type | Default | Description | -| :-----------------------------: | :------------------: | :--------: | :------------------------------------------------------- | -| [**`injectType`**](#injecttype) | `{String}` | `styleTag` | Allows to setup how styles will be injected into the DOM | -| [**`attributes`**](#attributes) | `{Object}` | `{}` | Adds custom attributes to tag | -| [**`insert`**](#insert) | `{String\|Function}` | `head` | Inserts tag at the given position into the DOM | -| [**`base`**](#base) | `{Number}` | `true` | Sets module ID base (DLLPlugin) | -| [**`esModule`**](#esmodule) | `{Boolean}` | `false` | Use ES modules syntax | +| Name | Type | Default | Description | +| :-----------------------------: | :------------------: | :---------: | :------------------------------------------------------- | +| [**`injectType`**](#injecttype) | `{String}` | `styleTag` | Allows to setup how styles will be injected into the DOM | +| [**`attributes`**](#attributes) | `{Object}` | `{}` | Adds custom attributes to tag | +| [**`insert`**](#insert) | `{String\|Function}` | `head` | Inserts tag at the given position into the DOM | +| [**`base`**](#base) | `{Number}` | `true` | Sets module ID base (DLLPlugin) | +| [**`esModule`**](#esmodule) | `{Boolean}` | `false` | Use ES modules syntax | +| [**`modules`**](#modules) | `{Object}` | `undefined` | Configuration CSS Modules | ### `injectType` @@ -580,6 +581,81 @@ module.exports = { }; ``` +### `modules` + +Type: `Object` +Default: `undefined` + +Configuration CSS Modules. + +#### `namedExport` + +Type: `Boolean` +Default: `false` + +Enables/disables ES modules named export for locals. + +> ⚠ Names of locals are converted to `camelCase`. + +> ⚠ It is not allowed to use JavaScript reserved words in css class names. + +> ⚠ Options `esModule` and `modules.namedExport` in `css-loader` and `style-loader` should be enabled. + +**styles.css** + +```css +.foo-baz { + color: red; +} +.bar { + color: blue; +} +``` + +**index.js** + +```js +import { fooBaz, bar } from './styles.css'; + +console.log(fooBaz, bar); +``` + +You can enable a ES module named export using: + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/, + use: [ + { + loader: 'style-loader', + options: { + esModule: true, + modules: { + namedExport: true, + }, + }, + }, + { + loader: 'css-loader', + options: { + esModule: true, + modules: { + namedExport: true, + }, + }, + }, + ], + }, + ], + }, +}; +``` + ## Examples ### Source maps diff --git a/src/index.js b/src/index.js index 27499db9..3bbc0cb6 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,8 @@ loaderApi.pitch = function loader(request) { const injectType = options.injectType || 'styleTag'; const esModule = typeof options.esModule !== 'undefined' ? options.esModule : false; + const namedExport = + esModule && options.modules && options.modules.namedExport; const runtimeOptions = { injectType: options.injectType, attributes: options.attributes, @@ -104,20 +106,22 @@ ${esModule ? 'export default {}' : ''}`; if (module.hot) { if (!content.locals || module.hot.invalidate) { var isEqualLocals = ${isEqualLocals.toString()}; - var oldLocals = content.locals; + var oldLocals = ${namedExport ? 'locals' : 'content.locals'}; module.hot.accept( ${loaderUtils.stringifyRequest(this, `!!${request}`)}, function () { ${ esModule - ? `if (!isEqualLocals(oldLocals, content.locals)) { + ? `if (!isEqualLocals(oldLocals, ${ + namedExport ? 'locals' : 'content.locals' + }, ${namedExport})) { module.hot.invalidate(); return; } - oldLocals = content.locals; + oldLocals = ${namedExport ? 'locals' : 'content.locals'}; if (update && refs > 0) { update(content); @@ -159,10 +163,9 @@ if (module.hot) { this, `!${path.join(__dirname, 'runtime/injectStylesIntoStyleTag.js')}` )}; - import content from ${loaderUtils.stringifyRequest( - this, - `!!${request}` - )};` + import content${ + namedExport ? ', * as locals' : '' + } from ${loaderUtils.stringifyRequest(this, `!!${request}`)};` : `var api = require(${loaderUtils.stringifyRequest( this, `!${path.join(__dirname, 'runtime/injectStylesIntoStyleTag.js')}` @@ -188,7 +191,7 @@ options.singleton = ${isSingleton}; var exported = {}; -exported.locals = content.locals || {}; +${namedExport ? '' : 'exported.locals = content.locals || {};'} exported.use = function() { if (!(refs++)) { update = api(content, options); @@ -205,7 +208,20 @@ exported.unuse = function() { ${hmrCode} -${esModule ? 'export default' : 'module.exports ='} exported;`; +${ + esModule + ? `${ + namedExport + ? `export * from ${loaderUtils.stringifyRequest( + this, + `!!${request}` + )};` + : '' + }; + export default exported;` + : 'module.exports = exported;' +} +`; } case 'styleTag': @@ -218,20 +234,22 @@ ${esModule ? 'export default' : 'module.exports ='} exported;`; if (module.hot) { if (!content.locals || module.hot.invalidate) { var isEqualLocals = ${isEqualLocals.toString()}; - var oldLocals = content.locals; + var oldLocals = ${namedExport ? 'locals' : 'content.locals'}; module.hot.accept( ${loaderUtils.stringifyRequest(this, `!!${request}`)}, function () { ${ esModule - ? `if (!isEqualLocals(oldLocals, content.locals)) { + ? `if (!isEqualLocals(oldLocals, ${ + namedExport ? 'locals' : 'content.locals' + }, ${namedExport})) { module.hot.invalidate(); return; } - oldLocals = content.locals; + oldLocals = ${namedExport ? 'locals' : 'content.locals'}; update(content);` : `content = require(${loaderUtils.stringifyRequest( @@ -271,10 +289,9 @@ if (module.hot) { this, `!${path.join(__dirname, 'runtime/injectStylesIntoStyleTag.js')}` )}; - import content from ${loaderUtils.stringifyRequest( - this, - `!!${request}` - )};` + import content${ + namedExport ? ', * as locals' : '' + } from ${loaderUtils.stringifyRequest(this, `!!${request}`)};` : `var api = require(${loaderUtils.stringifyRequest( this, `!${path.join(__dirname, 'runtime/injectStylesIntoStyleTag.js')}` @@ -300,7 +317,13 @@ var update = api(content, options); ${hmrCode} -${esModule ? 'export default' : 'module.exports ='} content.locals || {};`; +${ + esModule + ? namedExport + ? `export * from ${loaderUtils.stringifyRequest(this, `!!${request}`)};` + : 'export default content.locals || {};' + : 'module.exports = content.locals || {};' +}`; } } }; diff --git a/src/options.json b/src/options.json index 76f85495..5478177e 100644 --- a/src/options.json +++ b/src/options.json @@ -33,6 +33,16 @@ "esModule": { "description": "Use the ES modules syntax (https://github.com/webpack-contrib/css-loader#esmodule).", "type": "boolean" + }, + "modules": { + "type": "object", + "additionalProperties": false, + "properties": { + "namedExport": { + "description": "Enables/disables ES modules named export for locals (https://webpack.js.org/plugins/mini-css-extract-plugin/#namedexport).", + "type": "boolean" + } + } } }, "additionalProperties": false diff --git a/src/runtime/isEqualLocals.js b/src/runtime/isEqualLocals.js index 1002ff1f..12b2626d 100644 --- a/src/runtime/isEqualLocals.js +++ b/src/runtime/isEqualLocals.js @@ -1,4 +1,4 @@ -function isEqualLocals(a, b) { +function isEqualLocals(a, b, isNamedExport) { if ((!a && b) || (a && !b)) { return false; } @@ -6,12 +6,22 @@ function isEqualLocals(a, b) { let p; for (p in a) { + if (isNamedExport && p === 'default') { + // eslint-disable-next-line no-continue + continue; + } + if (a[p] !== b[p]) { return false; } } for (p in b) { + if (isNamedExport && p === 'default') { + // eslint-disable-next-line no-continue + continue; + } + if (!a[p]) { return false; } diff --git a/test/__snapshots__/modules-option.test.js.snap b/test/__snapshots__/modules-option.test.js.snap new file mode 100644 index 00000000..af40d8dd --- /dev/null +++ b/test/__snapshots__/modules-option.test.js.snap @@ -0,0 +1,93 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`"modules" option should work with the "lazySingletonStyleTag" inject type: DOM 1`] = ` +" + style-loader test + + + +

Body

+
+ + + +
Water
Ground
" +`; + +exports[`"modules" option should work with the "lazySingletonStyleTag" inject type: errors 1`] = `Array []`; + +exports[`"modules" option should work with the "lazySingletonStyleTag" inject type: warnings 1`] = `Array []`; + +exports[`"modules" option should work with the "lazyStyleTag" inject type: DOM 1`] = ` +" + style-loader test + + + +

Body

+
+ + + +
Water
Ground
" +`; + +exports[`"modules" option should work with the "lazyStyleTag" inject type: errors 1`] = `Array []`; + +exports[`"modules" option should work with the "lazyStyleTag" inject type: warnings 1`] = `Array []`; + +exports[`"modules" option should work with the "singletonStyleTag" inject type: DOM 1`] = ` +" + style-loader test + + + +

Body

+
+ + + +
Water
Ground
" +`; + +exports[`"modules" option should work with the "singletonStyleTag" inject type: errors 1`] = `Array []`; + +exports[`"modules" option should work with the "singletonStyleTag" inject type: warnings 1`] = `Array []`; + +exports[`"modules" option should work with the "styleTag" inject type: DOM 1`] = ` +" + style-loader test + + + +

Body

+
+ + + +
Water
Ground
" +`; + +exports[`"modules" option should work with the "styleTag" inject type: errors 1`] = `Array []`; + +exports[`"modules" option should work with the "styleTag" inject type: warnings 1`] = `Array []`; diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index 8e62f1b2..5cd90db8 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -30,50 +30,62 @@ exports[`validate options should throw an error on the "insert" option with "tru * options.insert should be an instance of function." `; +exports[`validate options should throw an error on the "modules" option with "true" value 1`] = ` +"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. + - options.modules should be an object: + object { namedExport? }" +`; + +exports[`validate options should throw an error on the "modules" option with "true" value 2`] = ` +"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. + - options.modules should be an object: + object { namedExport? }" +`; + exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = ` "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { injectType?, attributes?, insert?, base?, esModule? }" + object { injectType?, attributes?, insert?, base?, esModule?, modules? }" `; diff --git a/test/fixtures/named-export.css b/test/fixtures/named-export.css new file mode 100644 index 00000000..f6a6e752 --- /dev/null +++ b/test/fixtures/named-export.css @@ -0,0 +1,8 @@ +:local(.myClassName) { + background: red; +} + +:local(.myComposingClass) { + composes: className from './css-modules-local-scoped.css'; + color: blue; +} diff --git a/test/fixtures/named-export.js b/test/fixtures/named-export.js new file mode 100644 index 00000000..9fbacf7a --- /dev/null +++ b/test/fixtures/named-export.js @@ -0,0 +1,17 @@ +import { myClassName, myComposingClass } from './named-export.css'; + +const node1 = document.createElement("DIV"); +const textNode1 = document.createTextNode("Water"); + +node1.appendChild(textNode1); +node1.className = myClassName; + +document.body.appendChild(node1); + +const node2 = document.createElement("DIV"); +const textNode2 = document.createTextNode("Ground"); + +node2.appendChild(textNode2); +node2.className = myComposingClass; + +document.body.appendChild(node2); diff --git a/test/manual/src/index.js b/test/manual/src/index.js index 91916e9d..4f54d7b9 100644 --- a/test/manual/src/index.js +++ b/test/manual/src/index.js @@ -17,6 +17,18 @@ import two from './modules/two.module.css'; import toolbar from './modules/toolbar.module.css'; import page from './modules/page.module.css'; import toogle from './toogle.lazy.css'; +import { + namedExportRed, + namedExportGreen, + namedExportBlue, + namedExportBackground, +} from './style.named-export.module.css'; +import api2, { + namedExportLazyRed, + namedExportLazyGreen, + namedExportLazyBlue, + namedExportLazyBackground, +} from './style.named-export.lazy.module.css'; console.log('___LOCALS___'); console.log(component); @@ -140,3 +152,91 @@ button.addEventListener('click', () => { const toggleSection = document.getElementById('toggle-section'); toggleSection.appendChild(button); + +console.log('___NAMED_EXPORT___'); +console.log( + namedExportRed, + namedExportGreen, + namedExportBlue, + namedExportBackground +); + +const articleElement3 = document.createElement('article'); +const h3Element3 = document.createElement('h3'); +const h3TextNode3 = document.createTextNode('Named export'); + +const divElement9 = document.createElement('div'); +const divElement1Content1 = document.createTextNode('Red'); + +divElement9.className = namedExportRed; +divElement9.appendChild(divElement1Content1); + +const divElement10 = document.createElement('div'); +const divElement2Content1 = document.createTextNode('Green'); + +divElement10.className = namedExportGreen; +divElement10.appendChild(divElement2Content1); + +const divElement11 = document.createElement('div'); +const divElement3Content1 = document.createTextNode('Blue'); + +divElement11.className = namedExportBlue; +divElement11.appendChild(divElement3Content1); + +const divElement12 = document.createElement('div'); + +divElement12.className = namedExportBackground; + +h3Element3.appendChild(h3TextNode3); +articleElement3.appendChild(h3Element3); +articleElement3.appendChild(divElement9); +articleElement3.appendChild(divElement10); +articleElement3.appendChild(divElement11); +articleElement3.appendChild(divElement12); + +document.querySelectorAll('section')[0].appendChild(articleElement3); + +console.log('___LAZY_NAMED_EXPORT___'); +console.log( + namedExportLazyRed, + namedExportLazyGreen, + namedExportLazyBlue, + namedExportLazyBackground +); + +api2.use(); + +const articleElement4 = document.createElement('article'); +const h3Element4 = document.createElement('h3'); +const h3TextNode4 = document.createTextNode('Named export'); + +const divElement13 = document.createElement('div'); +const divElement5Content1 = document.createTextNode('Red'); + +divElement13.className = namedExportLazyRed; +divElement13.appendChild(divElement5Content1); + +const divElement14 = document.createElement('div'); +const divElement6Content2 = document.createTextNode('Green'); + +divElement14.className = namedExportLazyGreen; +divElement14.appendChild(divElement6Content2); + +const divElement15 = document.createElement('div'); +const divElement7Content2 = document.createTextNode('Blue'); + +divElement15.className = namedExportLazyBlue; +divElement15.appendChild(divElement7Content2); + +const divElement16 = document.createElement('div'); + +divElement16.className = namedExportLazyBackground; + +h3Element4.appendChild(h3TextNode4); +articleElement4.appendChild(h3Element4); +articleElement4.appendChild(divElement13); +articleElement4.appendChild(divElement14); +articleElement4.appendChild(divElement15); +articleElement4.appendChild(divElement16); + +document.querySelectorAll('section')[1].appendChild(articleElement4); diff --git a/test/manual/src/style.named-export.lazy.module.css b/test/manual/src/style.named-export.lazy.module.css new file mode 100644 index 00000000..6c60041b --- /dev/null +++ b/test/manual/src/style.named-export.lazy.module.css @@ -0,0 +1,16 @@ +.named-export-lazy-red { + color: red; +} + +.named-export-lazy-green { + color: green; +} + +.named-export-lazy-blue { + color: blue; +} + +.named-export-lazy-background { + height: 1200px; + background: url('./logo.png') center no-repeat; +} diff --git a/test/manual/src/style.named-export.module.css b/test/manual/src/style.named-export.module.css new file mode 100644 index 00000000..d518c215 --- /dev/null +++ b/test/manual/src/style.named-export.module.css @@ -0,0 +1,16 @@ +.named-export-red { + color: red; +} + +.named-export-green { + color: green; +} + +.named-export-blue { + color: blue; +} + +.named-export-background { + height: 1200px; + background: url('./logo.png') center no-repeat; +} diff --git a/test/manual/webpack.config.js b/test/manual/webpack.config.js index af5547a5..2e8805b8 100644 --- a/test/manual/webpack.config.js +++ b/test/manual/webpack.config.js @@ -50,7 +50,13 @@ module.exports = { }, { test: /\.module\.css$/i, - exclude: [/\.lazy\.css$/i, /\.link\.css$/i, /\.lazy\.module\.css$/i], + exclude: [ + /\.lazy\.css$/i, + /\.link\.css$/i, + /\.lazy\.module\.css$/i, + /\.named-export\.module\.css$/i, + /\.named-export\.lazy\.module\.css$/i, + ], use: [ { loader: require.resolve('../../dist/cjs.js'), @@ -84,6 +90,7 @@ module.exports = { }, { test: /\.lazy\.module\.css$/i, + exclude: [/\.named-export\.lazy\.module\.css$/i], use: [ { loader: require.resolve('../../dist/cjs.js'), @@ -99,7 +106,6 @@ module.exports = { }, ], }, - { test: /\.link\.css$/i, use: [ @@ -162,7 +168,55 @@ module.exports = { }, ], }, - + { + test: /\.named-export\.module\.css$/i, + use: [ + { + loader: require.resolve('../../dist/cjs.js'), + options: { + esModule: true, + modules: { + namedExport: true, + }, + }, + }, + { + loader: 'css-loader', + options: { + sourceMap: ENABLE_SOURCE_MAP, + esModule: true, + modules: { + namedExport: true, + }, + }, + }, + ], + }, + { + test: /\.named-export\.lazy\.module\.css$/i, + use: [ + { + loader: require.resolve('../../dist/cjs.js'), + options: { + injectType: 'lazyStyleTag', + esModule: true, + modules: { + namedExport: true, + }, + }, + }, + { + loader: 'css-loader', + options: { + sourceMap: ENABLE_SOURCE_MAP, + esModule: true, + modules: { + namedExport: true, + }, + }, + }, + ], + }, { test: /\.png$/i, loader: 'file-loader', diff --git a/test/modules-option.test.js b/test/modules-option.test.js new file mode 100644 index 00000000..c51c5794 --- /dev/null +++ b/test/modules-option.test.js @@ -0,0 +1,64 @@ +import path from 'path'; + +import { + compile, + getCompiler, + getErrors, + getWarnings, + runInJsDom, +} from './helpers/index'; + +describe('"modules" option', () => { + const injectTypes = [ + 'styleTag', + 'singletonStyleTag', + 'lazyStyleTag', + 'lazySingletonStyleTag', + ]; + + injectTypes.forEach((injectType) => { + it(`should work with the "${injectType}" inject type`, async () => { + expect.assertions(3); + + const compiler = getCompiler( + './named-export.js', + {}, + { + module: { + rules: [ + { + test: /\.css$/i, + use: [ + { + loader: path.resolve(__dirname, '../src/cjs.js'), + options: { + injectType, + esModule: true, + modules: { namedExport: true }, + }, + }, + { + loader: 'css-loader', + options: { + modules: { + namedExport: true, + }, + }, + }, + ], + }, + ], + }, + } + ); + const stats = await compile(compiler); + + runInJsDom('main.bundle.js', compiler, stats, (dom) => { + expect(dom.serialize()).toMatchSnapshot('DOM'); + }); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + }); +}); diff --git a/test/runtime/isEqualLocals.test.js b/test/runtime/isEqualLocals.test.js index 3871cd61..433e874b 100644 --- a/test/runtime/isEqualLocals.test.js +++ b/test/runtime/isEqualLocals.test.js @@ -44,5 +44,22 @@ describe('isEqualLocals', () => { // eslint-disable-next-line no-undefined expect(isEqualLocals({ foo: undefined }, { foo: 'bar' })).toBe(false); expect(isEqualLocals({ foo: { foo: 'bar' } }, { foo: 'bar' })).toBe(false); + + expect(isEqualLocals({ foo: 'bar' }, { foo: 'bar' }, true)).toBe(true); + expect(isEqualLocals({ foo: 'bar' }, { foo: 'baz' }, true)).toBe(false); + expect( + isEqualLocals( + { default: 'foo', foo: 'bar' }, + { default: 'bar', foo: 'bar' }, + true + ) + ).toBe(true); + expect( + isEqualLocals( + { default: 'foo', foo: 'bar' }, + { default: 'bar', foo: 'baz' }, + true + ) + ).toBe(false); }); }); diff --git a/test/validate-options.test.js b/test/validate-options.test.js index b066a5a7..b9db060e 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -24,6 +24,10 @@ describe('validate options', () => { success: [true, false], failure: ['true'], }, + modules: { + success: [{ namedExport: true }], + failure: [true, 'true'], + }, unknown: { success: [], failure: [1, true, false, 'test', /test/, [], {}, { foo: 'bar' }],