From 791fff36c8aaceaaa27e8665172008c28ad11484 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Wed, 22 Jul 2020 22:43:01 +0300 Subject: [PATCH] refactor: named export (#1125) --- README.md | 9 +- src/index.js | 14 +- src/utils.js | 61 ++++--- .../esModule-option.test.js.snap | 136 --------------- .../__snapshots__/modules-option.test.js.snap | 160 ++++++++++++++++++ test/esModule-option.test.js | 83 --------- .../fixtures/es-module/named/nested/index.css | 5 - .../namedExport}/base/index.css | 0 .../namedExport}/base/index.js | 0 .../namedExport}/broken/index.css | 0 .../namedExport}/broken/index.js | 0 .../modules/namedExport/nested/index.css | 20 +++ .../namedExport}/nested/index.js | 0 .../namedExport}/template/index.css | 0 .../namedExport}/template/index.js | 0 .../namedExport}/template/template.js | 0 test/modules-option.test.js | 97 +++++++++++ 17 files changed, 327 insertions(+), 258 deletions(-) delete mode 100644 test/fixtures/es-module/named/nested/index.css rename test/fixtures/{es-module/named => modules/namedExport}/base/index.css (100%) rename test/fixtures/{es-module/named => modules/namedExport}/base/index.js (100%) rename test/fixtures/{es-module/named => modules/namedExport}/broken/index.css (100%) rename test/fixtures/{es-module/named => modules/namedExport}/broken/index.js (100%) create mode 100644 test/fixtures/modules/namedExport/nested/index.css rename test/fixtures/{es-module/named => modules/namedExport}/nested/index.js (100%) rename test/fixtures/{es-module/named => modules/namedExport}/template/index.css (100%) rename test/fixtures/{es-module/named => modules/namedExport}/template/index.js (100%) rename test/fixtures/{es-module/named => modules/namedExport}/template/template.js (100%) diff --git a/README.md b/README.md index 7dcaae67..7548eae7 100644 --- a/README.md +++ b/README.md @@ -844,9 +844,10 @@ Type: `Boolean` Default: `false` Enables/disables ES modules named export for locals. -Names of locals are converted to camelCase. -> i It is not allowed to use JavaScript reserved words in css class names +> ⚠ Names of locals are converted to camelcase, i.e. the `exportLocalsConvention` option has `camelCaseOnly` value by default. + +> ⚠ It is not allowed to use JavaScript reserved words in css class names. **styles.css** @@ -920,12 +921,14 @@ module.exports = { ##### `exportlocalsConvention` Type: `String` -Default: `'asIs'` +Default: based on the `modules.namedExport` option value, if `true` - `camelCaseOnly`, otherwise `asIs` Style of exported class names. By default, the exported JSON keys mirror the class names (i.e `asIs` value). +> ⚠ Only `camelCaseOnly` value allowed if you set the `namedExport` value to `true`. + | Name | Type | Description | | :-------------------: | :--------: | :----------------------------------------------------------------------------------------------- | | **`'asIs'`** | `{String}` | Class names will be exported as is. | diff --git a/src/index.js b/src/index.js index d7e75f88..0ad95e72 100644 --- a/src/index.js +++ b/src/index.js @@ -36,7 +36,17 @@ export default async function loader(content, map, meta) { }); const plugins = []; - const options = normalizeOptions(rawOptions, this); + const callback = this.async(); + + let options; + + try { + options = normalizeOptions(rawOptions, this); + } catch (error) { + callback(error); + + return; + } if (shouldUseModulesPlugins(options)) { const icssResolver = this.getResolve({ @@ -117,8 +127,6 @@ export default async function loader(content, map, meta) { } } - const callback = this.async(); - let result; try { diff --git a/src/utils.js b/src/utils.js index 6441cc7f..6b5eb5fc 100644 --- a/src/utils.js +++ b/src/utils.js @@ -163,6 +163,13 @@ function getModulesOptions(rawOptions, loaderContext) { return false; } } + + if ( + rawOptions.modules.namedExport === true && + typeof rawOptions.modules.exportLocalsConvention === 'undefined' + ) { + modulesOptions.exportLocalsConvention = 'camelCaseOnly'; + } } modulesOptions = { ...modulesOptions, ...(rawOptions.modules || {}) }; @@ -172,12 +179,18 @@ function getModulesOptions(rawOptions, loaderContext) { modulesOptions.mode = modulesOptions.mode(loaderContext.resourcePath); } - if (modulesOptions.namedExport === true && rawOptions.esModule === false) { - loaderContext.emitError( - new Error( - '`Options.module.namedExport` cannot be used without `options.esModule`' - ) - ); + if (modulesOptions.namedExport === true) { + if (rawOptions.esModule === false) { + throw new Error( + 'The "modules.namedExport" option requires the "esModules" option to be enabled' + ); + } + + if (modulesOptions.exportLocalsConvention !== 'camelCaseOnly') { + throw new Error( + 'The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "camelCaseOnly"' + ); + } } return modulesOptions; @@ -411,19 +424,18 @@ function dashesCamelCase(str) { function getExportCode(exports, icssReplacements, options) { let code = ''; let localsCode = ''; - let namedCode = ''; const addExportToLocalsCode = (name, value) => { - if (localsCode) { - localsCode += `,\n`; - } - - localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`; - if (options.modules.namedExport) { - namedCode += `export const ${camelCase(name)} = ${JSON.stringify( + localsCode += `export const ${camelCase(name)} = ${JSON.stringify( value )};\n`; + } else { + if (localsCode) { + localsCode += `,\n`; + } + + localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`; } }; @@ -467,25 +479,18 @@ function getExportCode(exports, icssReplacements, options) { for (const replacement of icssReplacements) { const { replacementName, importName, localName } = replacement; - localsCode = localsCode.replace( - new RegExp(replacementName, 'g'), - () => `" + ${importName}.locals[${JSON.stringify(localName)}] + "` - ); - - if (options.modules.namedExport) { - namedCode = namedCode.replace( - new RegExp(replacementName, 'g'), - () => - `" + ${importName}_NAMED___[${JSON.stringify( + localsCode = localsCode.replace(new RegExp(replacementName, 'g'), () => + options.modules.namedExport + ? `" + ${importName}_NAMED___[${JSON.stringify( camelCase(localName) )}] + "` - ); - } + : `" + ${importName}.locals[${JSON.stringify(localName)}] + "` + ); } if (localsCode) { - code += namedCode - ? `${namedCode}\n` + code += options.modules.namedExport + ? `${localsCode}` : `___CSS_LOADER_EXPORT___.locals = {\n${localsCode}\n};\n`; } diff --git a/test/__snapshots__/esModule-option.test.js.snap b/test/__snapshots__/esModule-option.test.js.snap index d749a592..f6efb7f9 100644 --- a/test/__snapshots__/esModule-option.test.js.snap +++ b/test/__snapshots__/esModule-option.test.js.snap @@ -1,68 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`"esModule" option should emit error when class has unsupported name: errors 1`] = ` -Array [ - "ModuleParseError: Module parse failed: Unexpected keyword 'class' (7:13) -File was processed with these loaders:", -] -`; - -exports[`"esModule" option should emit error when class has unsupported name: warnings 1`] = `Array []`; - -exports[`"esModule" option should emit error when namedExport true && esModule false: errors 1`] = ` -Array [ - "ModuleError: Module Error (from \`replaced original path\`): -\`Options.module.namedExport\` cannot be used without \`options.esModule\`", -] -`; - -exports[`"esModule" option should work js template with "namedExport" option: errors 1`] = `Array []`; - -exports[`"esModule" option should work js template with "namedExport" option: module 1`] = ` -"// Imports -import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\"; -var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); -// Module -___CSS_LOADER_EXPORT___.push([module.id, \\".header-baz {\\\\n color: red;\\\\n}\\\\n\\\\n.body {\\\\n color: coral;\\\\n}\\\\n\\\\n.footer {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]); -// Exports -export const headerBaz = \\"header-baz\\"; -export const body = \\"body\\"; -export const footer = \\"footer\\"; - -export default ___CSS_LOADER_EXPORT___; -" -`; - -exports[`"esModule" option should work js template with "namedExport" option: result 1`] = ` -Object { - "css": Array [ - Array [ - "./es-module/named/template/index.css", - ".header-baz { - color: red; -} - -.body { - color: coral; -} - -.footer { - color: blue; -} -", - "", - ], - ], - "html": " -
-
-
-", -} -`; - -exports[`"esModule" option should work js template with "namedExport" option: warnings 1`] = `Array []`; - exports[`"esModule" option should work when not specified: errors 1`] = `Array []`; exports[`"esModule" option should work when not specified: module 1`] = ` @@ -109,79 +46,6 @@ Array [ exports[`"esModule" option should work when not specified: warnings 1`] = `Array []`; -exports[`"esModule" option should work with "namedExport" option with nested import: errors 1`] = `Array []`; - -exports[`"esModule" option should work with "namedExport" option with nested import: module 1`] = ` -"// Imports -import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\"; -import ___CSS_LOADER_ICSS_IMPORT_0___, * as ___CSS_LOADER_ICSS_IMPORT_0____NAMED___ from \\"-!../../../../../src/index.js??[ident]!../../../modules/composes/values.css\\"; -var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); -___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true); -// Module -___CSS_LOADER_EXPORT___.push([module.id, \\"._1yYSY3W2VgnkKdMmuxCIL1 {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\";\\\\n}\\\\n\\", \\"\\"]); -// Exports -export const vDef = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\"\\"; -export const ghi = \\"_1yYSY3W2VgnkKdMmuxCIL1\\"; - -export default ___CSS_LOADER_EXPORT___; -" -`; - -exports[`"esModule" option should work with "namedExport" option with nested import: result 1`] = ` -Array [ - Array [ - "../../src/index.js?[ident]!./modules/composes/values.css", - " -", - "", - ], - Array [ - "./es-module/named/nested/index.css", - "._1yYSY3W2VgnkKdMmuxCIL1 { - color: red; -} -", - "", - ], -] -`; - -exports[`"esModule" option should work with "namedExport" option with nested import: warnings 1`] = `Array []`; - -exports[`"esModule" option should work with "namedExport" option: errors 1`] = `Array []`; - -exports[`"esModule" option should work with "namedExport" option: module 1`] = ` -"// Imports -import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\"; -var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); -// Module -___CSS_LOADER_EXPORT___.push([module.id, \\"._3yAjbn27wJ9GVH2M-pj-hs {\\\\n color: red;\\\\n}\\\\n\\\\n.bar {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]); -// Exports -export const barBaz = \\"_3yAjbn27wJ9GVH2M-pj-hs\\"; - -export default ___CSS_LOADER_EXPORT___; -" -`; - -exports[`"esModule" option should work with "namedExport" option: result 1`] = ` -Array [ - Array [ - "./es-module/named/base/index.css", - "._3yAjbn27wJ9GVH2M-pj-hs { - color: red; -} - -.bar { - color: red; -} -", - "", - ], -] -`; - -exports[`"esModule" option should work with "namedExport" option: warnings 1`] = `Array []`; - exports[`"esModule" option should work with a value equal to "false": errors 1`] = `Array []`; exports[`"esModule" option should work with a value equal to "false": module 1`] = ` diff --git a/test/__snapshots__/modules-option.test.js.snap b/test/__snapshots__/modules-option.test.js.snap index ec3f187b..b9cc001b 100644 --- a/test/__snapshots__/modules-option.test.js.snap +++ b/test/__snapshots__/modules-option.test.js.snap @@ -1442,6 +1442,33 @@ Error: Can't resolve './unresolved.css' in '/test/fixtures/modules/unresolved'", exports[`"modules" option should throw an error on unresolved import: warnings 1`] = `Array []`; +exports[`"modules" option should throw an error when class has unsupported name (JavaScript reserved words): errors 1`] = ` +Array [ + "ModuleParseError: Module parse failed: Unexpected keyword 'class' (7:13) +File was processed with these loaders:", +] +`; + +exports[`"modules" option should throw an error when class has unsupported name (JavaScript reserved words): warnings 1`] = `Array []`; + +exports[`"modules" option should throw an error when the "namedExport" is enabled and the "exportLocalsConvention" options has not "camelCaseOnly" value: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: The \\"modules.namedExport\\" option requires the \\"modules.exportLocalsConvention\\" option to be \\"camelCaseOnly\\"", +] +`; + +exports[`"modules" option should throw an error when the "namedExport" is enabled and the "exportLocalsConvention" options has not "camelCaseOnly" value: warnings 1`] = `Array []`; + +exports[`"modules" option should throw an error when the "namedExport" option is "true", but the "esModule" is "false": errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: The \\"modules.namedExport\\" option requires the \\"esModules\\" option to be enabled", +] +`; + +exports[`"modules" option should throw an error when the "namedExport" option is "true", but the "esModule" is "false": warnings 1`] = `Array []`; + exports[`"modules" option should work and correctly replace escaped symbols: errors 1`] = `Array []`; exports[`"modules" option should work and correctly replace escaped symbols: module 1`] = ` @@ -3603,6 +3630,52 @@ h1 #_14PgYX9AnCgTBatBghNmOB { exports[`"modules" option should work and support "pure" mode: warnings 1`] = `Array []`; +exports[`"modules" option should work js template with "namedExport" option: errors 1`] = `Array []`; + +exports[`"modules" option should work js template with "namedExport" option: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\".header-baz {\\\\n color: red;\\\\n}\\\\n\\\\n.body {\\\\n color: coral;\\\\n}\\\\n\\\\n.footer {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]); +// Exports +export const headerBaz = \\"header-baz\\"; +export const body = \\"body\\"; +export const footer = \\"footer\\"; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work js template with "namedExport" option: result 1`] = ` +Object { + "css": Array [ + Array [ + "./modules/namedExport/template/index.css", + ".header-baz { + color: red; +} + +.body { + color: coral; +} + +.footer { + color: blue; +} +", + "", + ], + ], + "html": " +
+
+
+", +} +`; + +exports[`"modules" option should work js template with "namedExport" option: warnings 1`] = `Array []`; + exports[`"modules" option should work with a modules.auto Function that returns "false": errors 1`] = `Array []`; exports[`"modules" option should work with a modules.auto Function that returns "false": module 1`] = ` @@ -11866,6 +11939,93 @@ Array [ exports[`"modules" option should work with the "auto" when it is "true": warnings 1`] = `Array []`; +exports[`"modules" option should work with the "namedExport" option with nested import: errors 1`] = `Array []`; + +exports[`"modules" option should work with the "namedExport" option with nested import: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\"; +import ___CSS_LOADER_ICSS_IMPORT_0___, * as ___CSS_LOADER_ICSS_IMPORT_0____NAMED___ from \\"-!../../../../../src/index.js??[ident]!../../composes/values.css\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); +___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\"._10XHvKY7SkdmM8ZvRFtWzU {\\\\n color: #BF4040;\\\\n padding: 0 10px;\\\\n}\\\\n\\\\n._1kluA9lYOATstWNhEJp14n {\\\\n color: black;\\\\n}\\\\n\\\\n@media (min-width: 960px) and (max-width: 1024px) {\\\\n ._10XHvKY7SkdmM8ZvRFtWzU {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\";\\\\n padding: 0 20px;\\\\n }\\\\n}\\\\n\\", \\"\\"]); +// Exports +export const vDef = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\"\\"; +export const vPrimary = \\"#BF4040\\"; +export const sBlack = \\"black-selector\\"; +export const mLarge = \\"(min-width: 960px)\\"; +export const header = \\"_10XHvKY7SkdmM8ZvRFtWzU\\"; +export const blackSelector = \\"_1kluA9lYOATstWNhEJp14n\\"; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work with the "namedExport" option with nested import: result 1`] = ` +Array [ + Array [ + "../../src/index.js?[ident]!./modules/composes/values.css", + " +", + "", + ], + Array [ + "./modules/namedExport/nested/index.css", + "._10XHvKY7SkdmM8ZvRFtWzU { + color: #BF4040; + padding: 0 10px; +} + +._1kluA9lYOATstWNhEJp14n { + color: black; +} + +@media (min-width: 960px) and (max-width: 1024px) { + ._10XHvKY7SkdmM8ZvRFtWzU { + color: red; + padding: 0 20px; + } +} +", + "", + ], +] +`; + +exports[`"modules" option should work with the "namedExport" option with nested import: warnings 1`] = `Array []`; + +exports[`"modules" option should work with the "namedExport" option: errors 1`] = `Array []`; + +exports[`"modules" option should work with the "namedExport" option: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\"._1DWrSJ4evr5bF-6Ojpv9nG {\\\\n color: red;\\\\n}\\\\n\\\\n.bar {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]); +// Exports +export const barBaz = \\"_1DWrSJ4evr5bF-6Ojpv9nG\\"; +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"modules" option should work with the "namedExport" option: result 1`] = ` +Array [ + Array [ + "./modules/namedExport/base/index.css", + "._1DWrSJ4evr5bF-6Ojpv9nG { + color: red; +} + +.bar { + color: red; +} +", + "", + ], +] +`; + +exports[`"modules" option should work with the "namedExport" option: warnings 1`] = `Array []`; + exports[`"modules" option should work with the \`exportGlobals\` option (the \`mode\` option is \`global\`): errors 1`] = `Array []`; exports[`"modules" option should work with the \`exportGlobals\` option (the \`mode\` option is \`global\`): module 1`] = ` diff --git a/test/esModule-option.test.js b/test/esModule-option.test.js index 592fcab1..3988d4d0 100644 --- a/test/esModule-option.test.js +++ b/test/esModule-option.test.js @@ -106,64 +106,6 @@ describe('"esModule" option', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); - it('should work with "namedExport" option', async () => { - const compiler = getCompiler('./es-module/named/base/index.js', { - esModule: true, - modules: { - namedExport: true, - }, - }); - const stats = await compile(compiler); - - expect( - getModuleSource('./es-module/named/base/index.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 "namedExport" option with nested import', async () => { - const compiler = getCompiler('./es-module/named/nested/index.js', { - esModule: true, - modules: { - namedExport: true, - }, - }); - const stats = await compile(compiler); - - expect( - getModuleSource('./es-module/named/nested/index.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 js template with "namedExport" option', async () => { - const compiler = getCompiler('./es-module/named/template/index.js', { - esModule: true, - modules: { - localIdentName: '[local]', - namedExport: true, - }, - }); - const stats = await compile(compiler); - - expect( - getModuleSource('./es-module/named/template/index.css', stats) - ).toMatchSnapshot('module'); - expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( - 'result' - ); - expect(getWarnings(stats)).toMatchSnapshot('warnings'); - expect(getErrors(stats)).toMatchSnapshot('errors'); - }); - const styleLoaderTests = [ { localLoaderMode: 'commonjs', @@ -296,29 +238,4 @@ describe('"esModule" option', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); } - - it('should emit error when class has unsupported name', async () => { - const compiler = getCompiler('./es-module/named/broken/index.js', { - esModule: true, - modules: { - namedExport: true, - }, - }); - const stats = await compile(compiler); - - expect(getWarnings(stats)).toMatchSnapshot('warnings'); - expect(getErrors(stats, true)).toMatchSnapshot('errors'); - }); - - it('should emit error when namedExport true && esModule false', async () => { - const compiler = getCompiler('./es-module/named/base/index.js', { - esModule: false, - modules: { - namedExport: true, - }, - }); - const stats = await compile(compiler); - - expect(getErrors(stats)).toMatchSnapshot('errors'); - }); }); diff --git a/test/fixtures/es-module/named/nested/index.css b/test/fixtures/es-module/named/nested/index.css deleted file mode 100644 index ff9669f1..00000000 --- a/test/fixtures/es-module/named/nested/index.css +++ /dev/null @@ -1,5 +0,0 @@ -@value v-def from '../../../modules/composes/values.css'; - -.ghi { - color: v-def; -} diff --git a/test/fixtures/es-module/named/base/index.css b/test/fixtures/modules/namedExport/base/index.css similarity index 100% rename from test/fixtures/es-module/named/base/index.css rename to test/fixtures/modules/namedExport/base/index.css diff --git a/test/fixtures/es-module/named/base/index.js b/test/fixtures/modules/namedExport/base/index.js similarity index 100% rename from test/fixtures/es-module/named/base/index.js rename to test/fixtures/modules/namedExport/base/index.js diff --git a/test/fixtures/es-module/named/broken/index.css b/test/fixtures/modules/namedExport/broken/index.css similarity index 100% rename from test/fixtures/es-module/named/broken/index.css rename to test/fixtures/modules/namedExport/broken/index.css diff --git a/test/fixtures/es-module/named/broken/index.js b/test/fixtures/modules/namedExport/broken/index.js similarity index 100% rename from test/fixtures/es-module/named/broken/index.js rename to test/fixtures/modules/namedExport/broken/index.js diff --git a/test/fixtures/modules/namedExport/nested/index.css b/test/fixtures/modules/namedExport/nested/index.css new file mode 100644 index 00000000..0d260864 --- /dev/null +++ b/test/fixtures/modules/namedExport/nested/index.css @@ -0,0 +1,20 @@ +@value v-def from '../../composes/values.css'; +@value v-primary: #BF4040; +@value s-black: black-selector; +@value m-large: (min-width: 960px); + +.header { + color: v-primary; + padding: 0 10px; +} + +.s-black { + color: black; +} + +@media m-large and (max-width: 1024px) { + .header { + color: v-def; + padding: 0 20px; + } +} diff --git a/test/fixtures/es-module/named/nested/index.js b/test/fixtures/modules/namedExport/nested/index.js similarity index 100% rename from test/fixtures/es-module/named/nested/index.js rename to test/fixtures/modules/namedExport/nested/index.js diff --git a/test/fixtures/es-module/named/template/index.css b/test/fixtures/modules/namedExport/template/index.css similarity index 100% rename from test/fixtures/es-module/named/template/index.css rename to test/fixtures/modules/namedExport/template/index.css diff --git a/test/fixtures/es-module/named/template/index.js b/test/fixtures/modules/namedExport/template/index.js similarity index 100% rename from test/fixtures/es-module/named/template/index.js rename to test/fixtures/modules/namedExport/template/index.js diff --git a/test/fixtures/es-module/named/template/template.js b/test/fixtures/modules/namedExport/template/template.js similarity index 100% rename from test/fixtures/es-module/named/template/template.js rename to test/fixtures/modules/namedExport/template/template.js diff --git a/test/modules-option.test.js b/test/modules-option.test.js index 158b7d78..cf6f1d46 100644 --- a/test/modules-option.test.js +++ b/test/modules-option.test.js @@ -1004,4 +1004,101 @@ describe('"modules" option', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should work with the "namedExport" option', async () => { + const compiler = getCompiler('./modules/namedExport/base/index.js', { + modules: { + namedExport: true, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/namedExport/base/index.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 the "namedExport" option with nested import', async () => { + const compiler = getCompiler('./modules/namedExport/nested/index.js', { + esModule: true, + modules: { + namedExport: true, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/namedExport/nested/index.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 js template with "namedExport" option', async () => { + const compiler = getCompiler('./modules/namedExport/template/index.js', { + esModule: true, + modules: { + localIdentName: '[local]', + namedExport: true, + }, + }); + const stats = await compile(compiler); + + expect( + getModuleSource('./modules/namedExport/template/index.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 throw an error when the "namedExport" option is "true", but the "esModule" is "false"', async () => { + const compiler = getCompiler('./modules/namedExport/base/index.js', { + esModule: false, + modules: { + namedExport: true, + }, + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); + + it('should throw an error when the "namedExport" is enabled and the "exportLocalsConvention" options has not "camelCaseOnly" value', async () => { + const compiler = getCompiler('./modules/namedExport/broken/index.js', { + esModule: true, + modules: { + namedExport: true, + exportLocalsConvention: 'dashes', + }, + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); + + it('should throw an error when class has unsupported name (JavaScript reserved words)', async () => { + const compiler = getCompiler('./modules/namedExport/broken/index.js', { + esModule: true, + modules: { + namedExport: true, + }, + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); });