From 1787dab814042c324e0f07e72bfeb55567bc7831 Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Wed, 28 Feb 2024 16:50:19 +0100 Subject: [PATCH 1/6] feat: add defaultExport option --- README.md | 42 +++++++++ src/index.js | 1 + src/loader-options.json | 5 + src/loader.js | 11 ++- .../validate-loader-options.test.js.snap | 16 ++-- .../expected/main.css | 12 +++ .../expected/main.js | 91 +++++++++++++++++++ .../es-named-export-with-default/index.js | 4 + .../es-named-export-with-default/style.css | 11 +++ .../webpack.config.js | 36 ++++++++ types/index.d.ts | 2 + 11 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 test/cases/es-named-export-with-default/expected/main.css create mode 100644 test/cases/es-named-export-with-default/expected/main.js create mode 100644 test/cases/es-named-export-with-default/index.js create mode 100644 test/cases/es-named-export-with-default/style.css create mode 100644 test/cases/es-named-export-with-default/webpack.config.js diff --git a/README.md b/README.md index 7d499a09..417fe6b6 100644 --- a/README.md +++ b/README.md @@ -407,6 +407,7 @@ module.exports = { - **[`publicPath`](#publicPath)** - **[`emit`](#emit)** - **[`esModule`](#esModule)** +- **[`defaultExport`](#defaultExport)** #### `publicPath` @@ -549,6 +550,47 @@ module.exports = { }; ``` +#### `defaultExport` + +Type: + +```ts +type defaultExport = boolean; +``` + +Default: `false` + +By default, `mini-css-extract-plugin` generates JS modules with a default export. +However for name exports, each local is exported as a named export. + +In case you need both default and named exports, you can enable this option: + +**webpack.config.js** + +```js +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +module.exports = { + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /\.css$/i, + use: [ + { + loader: MiniCssExtractPlugin.loader, + options: { + defaultExport: true, + }, + }, + "css-loader", + ], + }, + ], + }, +}; +``` + ## Examples ### Recommended diff --git a/src/index.js b/src/index.js index 51809608..3601833f 100644 --- a/src/index.js +++ b/src/index.js @@ -36,6 +36,7 @@ const { * @property {string | ((resourcePath: string, rootContext: string) => string)} [publicPath] * @property {boolean} [emit] * @property {boolean} [esModule] + * @property {boolean} [defaultExport] * @property {string} [layer] */ diff --git a/src/loader-options.json b/src/loader-options.json index 6d10fec2..6b333001 100644 --- a/src/loader-options.json +++ b/src/loader-options.json @@ -25,6 +25,11 @@ "description": "Generates JS modules that use the ES modules syntax.", "link": "https://github.com/webpack-contrib/mini-css-extract-plugin#esmodule" }, + "defaultExport": { + "type": "boolean", + "description": "Generates JS modules with the default export syntax even for named exports.", + "link": "https://github.com/webpack-contrib/mini-css-extract-plugin#defaultexports" + }, "layer": { "type": "string" } diff --git a/src/loader.js b/src/loader.js index 35d8a9ba..53b74984 100644 --- a/src/loader.js +++ b/src/loader.js @@ -124,6 +124,11 @@ function pitch(request) { const esModule = typeof options.esModule !== "undefined" ? options.esModule : true; + const defaultExport = + typeof options.defaultExport !== "undefined" + ? options.defaultExport + : false; + /** * @param {Dependency[] | [null, object][]} dependencies */ @@ -271,8 +276,10 @@ function pitch(request) { const exportsString = `export { ${identifiers .map(([id, key]) => `${id} as ${JSON.stringify(key)}`) .join(", ")} }`; - - return `${localsString}\n${exportsString}\n`; + const exportDefaultString = defaultExport + ? `export default ${JSON.stringify(locals)}\n` + : ""; + return `${localsString}\n${exportsString}\n${exportDefaultString}`; } return `\n${ diff --git a/test/__snapshots__/validate-loader-options.test.js.snap b/test/__snapshots__/validate-loader-options.test.js.snap index a47a0bad..9e9c63c2 100644 --- a/test/__snapshots__/validate-loader-options.test.js.snap +++ b/test/__snapshots__/validate-loader-options.test.js.snap @@ -21,47 +21,47 @@ exports[`validate options should throw an error on the "publicPath" option with exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, defaultExport?, layer? }" `; diff --git a/test/cases/es-named-export-with-default/expected/main.css b/test/cases/es-named-export-with-default/expected/main.css new file mode 100644 index 00000000..f0c5f7a2 --- /dev/null +++ b/test/cases/es-named-export-with-default/expected/main.css @@ -0,0 +1,12 @@ +.foo__style__aClass { + background: red; +} + +.foo__style__bClass { + color: green; +} + +.foo__style__cClass { + color: blue; +} + diff --git a/test/cases/es-named-export-with-default/expected/main.js b/test/cases/es-named-export-with-default/expected/main.js new file mode 100644 index 00000000..793d6b94 --- /dev/null +++ b/test/cases/es-named-export-with-default/expected/main.js @@ -0,0 +1,91 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ([ +/* 0 */, +/* 1 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ aClass: () => (/* binding */ _1), +/* harmony export */ bClass: () => (/* binding */ _2), +/* harmony export */ cClass: () => (/* binding */ _3) +/* harmony export */ }); +// extracted by mini-css-extract-plugin +var _1 = "foo__style__aClass"; +var _2 = "foo__style__bClass"; +var _3 = "foo__style__cClass"; + + + +/***/ }) +/******/ ]); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); + + +// eslint-disable-next-line no-console +console.log({ css: _style_css__WEBPACK_IMPORTED_MODULE_0__["default"], aClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.aClass, bClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.bClass, cClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.cClass }); + +})(); + +/******/ })() +; \ No newline at end of file diff --git a/test/cases/es-named-export-with-default/index.js b/test/cases/es-named-export-with-default/index.js new file mode 100644 index 00000000..ccbeff4d --- /dev/null +++ b/test/cases/es-named-export-with-default/index.js @@ -0,0 +1,4 @@ +import css, { aClass, bClass, cClass } from "./style.css"; + +// eslint-disable-next-line no-console +console.log({ css, aClass, bClass, cClass }); diff --git a/test/cases/es-named-export-with-default/style.css b/test/cases/es-named-export-with-default/style.css new file mode 100644 index 00000000..19d3d6be --- /dev/null +++ b/test/cases/es-named-export-with-default/style.css @@ -0,0 +1,11 @@ +.aClass { + background: red; +} + +.bClass { + color: green; +} + +.cClass { + color: blue; +} diff --git a/test/cases/es-named-export-with-default/webpack.config.js b/test/cases/es-named-export-with-default/webpack.config.js new file mode 100644 index 00000000..e14a75b8 --- /dev/null +++ b/test/cases/es-named-export-with-default/webpack.config.js @@ -0,0 +1,36 @@ +const Self = require("../../../"); + +module.exports = { + entry: "./index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [ + { + loader: Self.loader, + options: { + defaultExport: false, + }, + }, + { + loader: "css-loader", + options: { + esModule: true, + modules: { + namedExport: true, + exportLocalsConvention: "asIs", + localIdentName: "foo__[name]__[local]", + }, + }, + }, + ], + }, + ], + }, + plugins: [ + new Self({ + filename: "[name].css", + }), + ], +}; diff --git a/types/index.d.ts b/types/index.d.ts index 0765eb15..61142615 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -147,6 +147,7 @@ type PluginOptions = { * @property {string | ((resourcePath: string, rootContext: string) => string)} [publicPath] * @property {boolean} [emit] * @property {boolean} [esModule] + * @property {boolean} [defaultExport] * @property {string} [layer] */ /** @@ -199,6 +200,7 @@ type LoaderOptions = { | undefined; emit?: boolean | undefined; esModule?: boolean | undefined; + defaultExport?: boolean | undefined; layer?: string | undefined; }; type NormalizedPluginOptions = { From 023d85cd1c065783f08b3c6f0c003c7fcb5656a5 Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Thu, 29 Feb 2024 09:23:29 +0100 Subject: [PATCH 2/6] fix: improve esMododule exports --- README.md | 42 --------- src/index.js | 1 - src/loader-options.json | 5 - src/loader.js | 13 +-- .../validate-loader-options.test.js.snap | 16 ++-- .../expected/main.js | 4 +- .../expected/main.js | 9 +- .../expected/main.mjs | 4 +- .../es-named-export-as-is/expected/main.js | 4 +- .../expected/main.mjs | 4 +- .../expected/main.css | 12 --- .../expected/main.js | 91 ------------------- .../es-named-export-with-default/index.js | 4 - .../es-named-export-with-default/style.css | 11 --- .../webpack.config.js | 36 -------- test/cases/es-named-export/expected/main.js | 4 +- .../expected/main.js | 4 +- types/index.d.ts | 2 - 18 files changed, 37 insertions(+), 229 deletions(-) delete mode 100644 test/cases/es-named-export-with-default/expected/main.css delete mode 100644 test/cases/es-named-export-with-default/expected/main.js delete mode 100644 test/cases/es-named-export-with-default/index.js delete mode 100644 test/cases/es-named-export-with-default/style.css delete mode 100644 test/cases/es-named-export-with-default/webpack.config.js diff --git a/README.md b/README.md index 417fe6b6..7d499a09 100644 --- a/README.md +++ b/README.md @@ -407,7 +407,6 @@ module.exports = { - **[`publicPath`](#publicPath)** - **[`emit`](#emit)** - **[`esModule`](#esModule)** -- **[`defaultExport`](#defaultExport)** #### `publicPath` @@ -550,47 +549,6 @@ module.exports = { }; ``` -#### `defaultExport` - -Type: - -```ts -type defaultExport = boolean; -``` - -Default: `false` - -By default, `mini-css-extract-plugin` generates JS modules with a default export. -However for name exports, each local is exported as a named export. - -In case you need both default and named exports, you can enable this option: - -**webpack.config.js** - -```js -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); - -module.exports = { - plugins: [new MiniCssExtractPlugin()], - module: { - rules: [ - { - test: /\.css$/i, - use: [ - { - loader: MiniCssExtractPlugin.loader, - options: { - defaultExport: true, - }, - }, - "css-loader", - ], - }, - ], - }, -}; -``` - ## Examples ### Recommended diff --git a/src/index.js b/src/index.js index 3601833f..51809608 100644 --- a/src/index.js +++ b/src/index.js @@ -36,7 +36,6 @@ const { * @property {string | ((resourcePath: string, rootContext: string) => string)} [publicPath] * @property {boolean} [emit] * @property {boolean} [esModule] - * @property {boolean} [defaultExport] * @property {string} [layer] */ diff --git a/src/loader-options.json b/src/loader-options.json index 6b333001..6d10fec2 100644 --- a/src/loader-options.json +++ b/src/loader-options.json @@ -25,11 +25,6 @@ "description": "Generates JS modules that use the ES modules syntax.", "link": "https://github.com/webpack-contrib/mini-css-extract-plugin#esmodule" }, - "defaultExport": { - "type": "boolean", - "description": "Generates JS modules with the default export syntax even for named exports.", - "link": "https://github.com/webpack-contrib/mini-css-extract-plugin#defaultexports" - }, "layer": { "type": "string" } diff --git a/src/loader.js b/src/loader.js index 53b74984..b6f4c744 100644 --- a/src/loader.js +++ b/src/loader.js @@ -124,11 +124,6 @@ function pitch(request) { const esModule = typeof options.esModule !== "undefined" ? options.esModule : true; - const defaultExport = - typeof options.defaultExport !== "undefined" - ? options.defaultExport - : false; - /** * @param {Dependency[] | [null, object][]} dependencies */ @@ -276,10 +271,10 @@ function pitch(request) { const exportsString = `export { ${identifiers .map(([id, key]) => `${id} as ${JSON.stringify(key)}`) .join(", ")} }`; - const exportDefaultString = defaultExport - ? `export default ${JSON.stringify(locals)}\n` - : ""; - return `${localsString}\n${exportsString}\n${exportDefaultString}`; + const exportDefaultString = `export default ${JSON.stringify( + locals + )}`; + return `${localsString}\n${exportsString}\n${exportDefaultString}\n`; } return `\n${ diff --git a/test/__snapshots__/validate-loader-options.test.js.snap b/test/__snapshots__/validate-loader-options.test.js.snap index 9e9c63c2..a47a0bad 100644 --- a/test/__snapshots__/validate-loader-options.test.js.snap +++ b/test/__snapshots__/validate-loader-options.test.js.snap @@ -21,47 +21,47 @@ exports[`validate options should throw an error on the "publicPath" option with exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, defaultExport?, layer? }" + object { publicPath?, emit?, esModule?, layer? }" `; diff --git a/test/cases/custom-loader-with-functional-exports/expected/main.js b/test/cases/custom-loader-with-functional-exports/expected/main.js index e3e3d2ff..0e7d18db 100644 --- a/test/cases/custom-loader-with-functional-exports/expected/main.js +++ b/test/cases/custom-loader-with-functional-exports/expected/main.js @@ -8,12 +8,14 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ cnA: () => (/* binding */ _1), -/* harmony export */ cnB: () => (/* binding */ _2) +/* harmony export */ cnB: () => (/* binding */ _2), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = () => "class-name-a"; var _2 = () => "class-name-b"; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({}); /***/ }) diff --git a/test/cases/es-module-concatenation-modules/expected/main.js b/test/cases/es-module-concatenation-modules/expected/main.js index 918474db..14034b62 100644 --- a/test/cases/es-module-concatenation-modules/expected/main.js +++ b/test/cases/es-module-concatenation-modules/expected/main.js @@ -48,14 +48,16 @@ __webpack_require__.d(__webpack_exports__, { var a_namespaceObject = {}; __webpack_require__.r(a_namespaceObject); __webpack_require__.d(a_namespaceObject, { - a: () => (_1) + a: () => (_1), + "default": () => (a) }); // NAMESPACE OBJECT: ./b.css var b_namespaceObject = {}; __webpack_require__.r(b_namespaceObject); __webpack_require__.d(b_namespaceObject, { - b: () => (b_1) + b: () => (b_1), + "default": () => (b) }); // NAMESPACE OBJECT: ./index.js @@ -71,16 +73,19 @@ __webpack_require__.d(index_namespaceObject, { // extracted by mini-css-extract-plugin var _1 = "foo__a"; +/* harmony default export */ const a = ({"a":"foo__a"}); ;// CONCATENATED MODULE: ./b.css // extracted by mini-css-extract-plugin var b_1 = "foo__b"; +/* harmony default export */ const b = ({"b":"foo__b"}); ;// CONCATENATED MODULE: ./c.css // extracted by mini-css-extract-plugin var c_1 = "foo__c"; +/* harmony default export */ const c = ({"c":"foo__c"}); ;// CONCATENATED MODULE: ./index.js /* eslint-disable import/no-namespace */ diff --git a/test/cases/es-named-export-as-is-output-module/expected/main.mjs b/test/cases/es-named-export-as-is-output-module/expected/main.mjs index dd030372..3a6c09be 100644 --- a/test/cases/es-named-export-as-is-output-module/expected/main.mjs +++ b/test/cases/es-named-export-as-is-output-module/expected/main.mjs @@ -7,13 +7,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "a-class": () => (/* binding */ _1), /* harmony export */ b__class: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3) +/* harmony export */ cClass: () => (/* binding */ _3), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "Xh041yLR4iCP4RGjge50"; var _2 = "NMuRsxoDwvW8BhSXhFAY"; var _3 = "ayWIv09rPsAqE2JznIsI"; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"a-class":"Xh041yLR4iCP4RGjge50","b__class":"NMuRsxoDwvW8BhSXhFAY","cClass":"ayWIv09rPsAqE2JznIsI"}); /***/ }) diff --git a/test/cases/es-named-export-as-is/expected/main.js b/test/cases/es-named-export-as-is/expected/main.js index a17ffa68..a8b1d5fa 100644 --- a/test/cases/es-named-export-as-is/expected/main.js +++ b/test/cases/es-named-export-as-is/expected/main.js @@ -9,13 +9,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "a-class": () => (/* binding */ _1), /* harmony export */ b__class: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3) +/* harmony export */ cClass: () => (/* binding */ _3), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "Xh041yLR4iCP4RGjge50"; var _2 = "NMuRsxoDwvW8BhSXhFAY"; var _3 = "ayWIv09rPsAqE2JznIsI"; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"a-class":"Xh041yLR4iCP4RGjge50","b__class":"NMuRsxoDwvW8BhSXhFAY","cClass":"ayWIv09rPsAqE2JznIsI"}); /***/ }) diff --git a/test/cases/es-named-export-output-module/expected/main.mjs b/test/cases/es-named-export-output-module/expected/main.mjs index eea1b171..74d00fae 100644 --- a/test/cases/es-named-export-output-module/expected/main.mjs +++ b/test/cases/es-named-export-output-module/expected/main.mjs @@ -7,13 +7,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ aClass: () => (/* binding */ _1), /* harmony export */ bClass: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3) +/* harmony export */ cClass: () => (/* binding */ _3), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"aClass":"foo__style__a-class","bClass":"foo__style__b__class","cClass":"foo__style__cClass"}); /***/ }) diff --git a/test/cases/es-named-export-with-default/expected/main.css b/test/cases/es-named-export-with-default/expected/main.css deleted file mode 100644 index f0c5f7a2..00000000 --- a/test/cases/es-named-export-with-default/expected/main.css +++ /dev/null @@ -1,12 +0,0 @@ -.foo__style__aClass { - background: red; -} - -.foo__style__bClass { - color: green; -} - -.foo__style__cClass { - color: blue; -} - diff --git a/test/cases/es-named-export-with-default/expected/main.js b/test/cases/es-named-export-with-default/expected/main.js deleted file mode 100644 index 793d6b94..00000000 --- a/test/cases/es-named-export-with-default/expected/main.js +++ /dev/null @@ -1,91 +0,0 @@ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ([ -/* 0 */, -/* 1 */ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ aClass: () => (/* binding */ _1), -/* harmony export */ bClass: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3) -/* harmony export */ }); -// extracted by mini-css-extract-plugin -var _1 = "foo__style__aClass"; -var _2 = "foo__style__bClass"; -var _3 = "foo__style__cClass"; - - - -/***/ }) -/******/ ]); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. -(() => { -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); - - -// eslint-disable-next-line no-console -console.log({ css: _style_css__WEBPACK_IMPORTED_MODULE_0__["default"], aClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.aClass, bClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.bClass, cClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.cClass }); - -})(); - -/******/ })() -; \ No newline at end of file diff --git a/test/cases/es-named-export-with-default/index.js b/test/cases/es-named-export-with-default/index.js deleted file mode 100644 index ccbeff4d..00000000 --- a/test/cases/es-named-export-with-default/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import css, { aClass, bClass, cClass } from "./style.css"; - -// eslint-disable-next-line no-console -console.log({ css, aClass, bClass, cClass }); diff --git a/test/cases/es-named-export-with-default/style.css b/test/cases/es-named-export-with-default/style.css deleted file mode 100644 index 19d3d6be..00000000 --- a/test/cases/es-named-export-with-default/style.css +++ /dev/null @@ -1,11 +0,0 @@ -.aClass { - background: red; -} - -.bClass { - color: green; -} - -.cClass { - color: blue; -} diff --git a/test/cases/es-named-export-with-default/webpack.config.js b/test/cases/es-named-export-with-default/webpack.config.js deleted file mode 100644 index e14a75b8..00000000 --- a/test/cases/es-named-export-with-default/webpack.config.js +++ /dev/null @@ -1,36 +0,0 @@ -const Self = require("../../../"); - -module.exports = { - entry: "./index.js", - module: { - rules: [ - { - test: /\.css$/, - use: [ - { - loader: Self.loader, - options: { - defaultExport: false, - }, - }, - { - loader: "css-loader", - options: { - esModule: true, - modules: { - namedExport: true, - exportLocalsConvention: "asIs", - localIdentName: "foo__[name]__[local]", - }, - }, - }, - ], - }, - ], - }, - plugins: [ - new Self({ - filename: "[name].css", - }), - ], -}; diff --git a/test/cases/es-named-export/expected/main.js b/test/cases/es-named-export/expected/main.js index 08202354..6ddc762a 100644 --- a/test/cases/es-named-export/expected/main.js +++ b/test/cases/es-named-export/expected/main.js @@ -9,13 +9,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "a-class": () => (/* binding */ _1), /* harmony export */ b__class: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3) +/* harmony export */ cClass: () => (/* binding */ _3), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"a-class":"foo__style__a-class","b__class":"foo__style__b__class","cClass":"foo__style__cClass"}); /***/ }) diff --git a/test/cases/export-only-locals-and-es-named-export/expected/main.js b/test/cases/export-only-locals-and-es-named-export/expected/main.js index fc556c4f..aa67251d 100644 --- a/test/cases/export-only-locals-and-es-named-export/expected/main.js +++ b/test/cases/export-only-locals-and-es-named-export/expected/main.js @@ -9,13 +9,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ aClass: () => (/* binding */ _1), /* harmony export */ bClass: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3) +/* harmony export */ cClass: () => (/* binding */ _3), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"aClass":"foo__style__a-class","bClass":"foo__style__b__class","cClass":"foo__style__cClass"}); /***/ }) diff --git a/types/index.d.ts b/types/index.d.ts index 61142615..0765eb15 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -147,7 +147,6 @@ type PluginOptions = { * @property {string | ((resourcePath: string, rootContext: string) => string)} [publicPath] * @property {boolean} [emit] * @property {boolean} [esModule] - * @property {boolean} [defaultExport] * @property {string} [layer] */ /** @@ -200,7 +199,6 @@ type LoaderOptions = { | undefined; emit?: boolean | undefined; esModule?: boolean | undefined; - defaultExport?: boolean | undefined; layer?: string | undefined; }; type NormalizedPluginOptions = { From 9e1c005409e7fcfce7af678cd9f58ca641b8611f Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Fri, 1 Mar 2024 13:01:24 +0100 Subject: [PATCH 3/6] refactor: reuse export string --- src/loader.js | 6 +++--- .../custom-loader-with-functional-exports/expected/main.js | 2 +- test/cases/es-module-concatenation-modules/expected/main.js | 6 +++--- .../es-named-export-as-is-output-module/expected/main.mjs | 2 +- test/cases/es-named-export-as-is/expected/main.js | 2 +- test/cases/es-named-export-output-module/expected/main.mjs | 2 +- test/cases/es-named-export/expected/main.js | 2 +- .../export-only-locals-and-es-named-export/expected/main.js | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/loader.js b/src/loader.js index b6f4c744..c1f24ec1 100644 --- a/src/loader.js +++ b/src/loader.js @@ -271,9 +271,9 @@ function pitch(request) { const exportsString = `export { ${identifiers .map(([id, key]) => `${id} as ${JSON.stringify(key)}`) .join(", ")} }`; - const exportDefaultString = `export default ${JSON.stringify( - locals - )}`; + const exportDefaultString = `export default { ${identifiers + .map(([id, key]) => `${JSON.stringify(key)}: ${id}`) + .join(", ")} }`; return `${localsString}\n${exportsString}\n${exportDefaultString}\n`; } diff --git a/test/cases/custom-loader-with-functional-exports/expected/main.js b/test/cases/custom-loader-with-functional-exports/expected/main.js index 0e7d18db..8648479e 100644 --- a/test/cases/custom-loader-with-functional-exports/expected/main.js +++ b/test/cases/custom-loader-with-functional-exports/expected/main.js @@ -15,7 +15,7 @@ __webpack_require__.r(__webpack_exports__); var _1 = () => "class-name-a"; var _2 = () => "class-name-b"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "cnA": _1, "cnB": _2 }); /***/ }) diff --git a/test/cases/es-module-concatenation-modules/expected/main.js b/test/cases/es-module-concatenation-modules/expected/main.js index 14034b62..279943a9 100644 --- a/test/cases/es-module-concatenation-modules/expected/main.js +++ b/test/cases/es-module-concatenation-modules/expected/main.js @@ -73,19 +73,19 @@ __webpack_require__.d(index_namespaceObject, { // extracted by mini-css-extract-plugin var _1 = "foo__a"; -/* harmony default export */ const a = ({"a":"foo__a"}); +/* harmony default export */ const a = ({ "a": _1 }); ;// CONCATENATED MODULE: ./b.css // extracted by mini-css-extract-plugin var b_1 = "foo__b"; -/* harmony default export */ const b = ({"b":"foo__b"}); +/* harmony default export */ const b = ({ "b": b_1 }); ;// CONCATENATED MODULE: ./c.css // extracted by mini-css-extract-plugin var c_1 = "foo__c"; -/* harmony default export */ const c = ({"c":"foo__c"}); +/* harmony default export */ const c = ({ "c": c_1 }); ;// CONCATENATED MODULE: ./index.js /* eslint-disable import/no-namespace */ diff --git a/test/cases/es-named-export-as-is-output-module/expected/main.mjs b/test/cases/es-named-export-as-is-output-module/expected/main.mjs index 3a6c09be..b1a9cd9c 100644 --- a/test/cases/es-named-export-as-is-output-module/expected/main.mjs +++ b/test/cases/es-named-export-as-is-output-module/expected/main.mjs @@ -15,7 +15,7 @@ var _1 = "Xh041yLR4iCP4RGjge50"; var _2 = "NMuRsxoDwvW8BhSXhFAY"; var _3 = "ayWIv09rPsAqE2JznIsI"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"a-class":"Xh041yLR4iCP4RGjge50","b__class":"NMuRsxoDwvW8BhSXhFAY","cClass":"ayWIv09rPsAqE2JznIsI"}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/es-named-export-as-is/expected/main.js b/test/cases/es-named-export-as-is/expected/main.js index a8b1d5fa..260d0610 100644 --- a/test/cases/es-named-export-as-is/expected/main.js +++ b/test/cases/es-named-export-as-is/expected/main.js @@ -17,7 +17,7 @@ var _1 = "Xh041yLR4iCP4RGjge50"; var _2 = "NMuRsxoDwvW8BhSXhFAY"; var _3 = "ayWIv09rPsAqE2JznIsI"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"a-class":"Xh041yLR4iCP4RGjge50","b__class":"NMuRsxoDwvW8BhSXhFAY","cClass":"ayWIv09rPsAqE2JznIsI"}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/es-named-export-output-module/expected/main.mjs b/test/cases/es-named-export-output-module/expected/main.mjs index 74d00fae..ef9a39c9 100644 --- a/test/cases/es-named-export-output-module/expected/main.mjs +++ b/test/cases/es-named-export-output-module/expected/main.mjs @@ -15,7 +15,7 @@ var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"aClass":"foo__style__a-class","bClass":"foo__style__b__class","cClass":"foo__style__cClass"}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "aClass": _1, "bClass": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/es-named-export/expected/main.js b/test/cases/es-named-export/expected/main.js index 6ddc762a..4e92f660 100644 --- a/test/cases/es-named-export/expected/main.js +++ b/test/cases/es-named-export/expected/main.js @@ -17,7 +17,7 @@ var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"a-class":"foo__style__a-class","b__class":"foo__style__b__class","cClass":"foo__style__cClass"}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/export-only-locals-and-es-named-export/expected/main.js b/test/cases/export-only-locals-and-es-named-export/expected/main.js index aa67251d..ba7befb1 100644 --- a/test/cases/export-only-locals-and-es-named-export/expected/main.js +++ b/test/cases/export-only-locals-and-es-named-export/expected/main.js @@ -17,7 +17,7 @@ var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"aClass":"foo__style__a-class","bClass":"foo__style__b__class","cClass":"foo__style__cClass"}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "aClass": _1, "bClass": _2, "cClass": _3 }); /***/ }) From fe1faa330a210c3e9fb7657d8958830b4a68ff92 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Tue, 2 Apr 2024 20:24:08 +0300 Subject: [PATCH 4/6] refactor: code --- README.md | 55 +++++++++++++++++++ src/index.js | 1 + src/loader-options.json | 5 ++ src/loader.js | 15 +++-- .../validate-loader-options.test.js.snap | 16 +++--- test/validate-loader-options.test.js | 4 ++ types/index.d.ts | 2 + 7 files changed, 86 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7d499a09..461e7b1e 100644 --- a/README.md +++ b/README.md @@ -407,6 +407,7 @@ module.exports = { - **[`publicPath`](#publicPath)** - **[`emit`](#emit)** - **[`esModule`](#esModule)** +- **[`defaultExport`](#defaultExport)** #### `publicPath` @@ -549,6 +550,60 @@ module.exports = { }; ``` +#### `defaultExport` + +Type: + +```ts +type defaultExport = boolean; +``` + +Default: `false` + +> **Note** +> +> This options will work only when you set `namedExport` to `true` in `css-loader` + +By default, `mini-css-extract-plugin` generates JS modules based on the `esModule` and `namedExport` options in `css-loader`. +Using the `esModule` and `namedExport` options will allow you to better optimize your code. +If you set `esModule: true` and `namedExport: true` for `css-loader` `mini-css-extract-plugin` will generate **only** a named export. +Our official recommendation is to use only named export for better future compatibility. +But for some applications, it is not easy to quickly rewrite the code from the default export to a named export. + +In case you need both default and named exports, you can enable this option: + +**webpack.config.js** + +```js +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +module.exports = { + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /\.css$/i, + use: [ + { + loader: MiniCssExtractPlugin.loader, + options: { + defaultExport: true, + }, + }, + { + loader: "css-loader", + esModule: true, + modules: { + namedExport: true, + }, + }, + ], + }, + ], + }, +}; +``` + ## Examples ### Recommended diff --git a/src/index.js b/src/index.js index 51809608..ddfc327b 100644 --- a/src/index.js +++ b/src/index.js @@ -37,6 +37,7 @@ const { * @property {boolean} [emit] * @property {boolean} [esModule] * @property {string} [layer] + * @property {boolean} [defaultExport] */ /** diff --git a/src/loader-options.json b/src/loader-options.json index 6d10fec2..789f5045 100644 --- a/src/loader-options.json +++ b/src/loader-options.json @@ -27,6 +27,11 @@ }, "layer": { "type": "string" + }, + "defaultExport": { + "type": "boolean", + "description": "Duplicate the named export with CSS modules locals to the default export (only when `esModules: true` for css-loader).", + "link": "https://github.com/webpack-contrib/mini-css-extract-plugin#defaultexports" } } } diff --git a/src/loader.js b/src/loader.js index c1f24ec1..2e182aa5 100644 --- a/src/loader.js +++ b/src/loader.js @@ -271,10 +271,17 @@ function pitch(request) { const exportsString = `export { ${identifiers .map(([id, key]) => `${id} as ${JSON.stringify(key)}`) .join(", ")} }`; - const exportDefaultString = `export default { ${identifiers - .map(([id, key]) => `${JSON.stringify(key)}: ${id}`) - .join(", ")} }`; - return `${localsString}\n${exportsString}\n${exportDefaultString}\n`; + + const defaultExport = + typeof options.defaultExport !== "undefined" + ? options.defaultExport + : false; + + return defaultExport + ? `${localsString}\n${exportsString}\nexport default { ${identifiers + .map(([id, key]) => `${JSON.stringify(key)}: ${id}`) + .join(", ")} }\n` + : `${localsString}\n${exportsString}\n`; } return `\n${ diff --git a/test/__snapshots__/validate-loader-options.test.js.snap b/test/__snapshots__/validate-loader-options.test.js.snap index a47a0bad..ed9f18c3 100644 --- a/test/__snapshots__/validate-loader-options.test.js.snap +++ b/test/__snapshots__/validate-loader-options.test.js.snap @@ -21,47 +21,47 @@ exports[`validate options should throw an error on the "publicPath" option with exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin 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 { publicPath?, emit?, esModule?, layer? }" + object { publicPath?, emit?, esModule?, layer?, defaultExport? }" `; diff --git a/test/validate-loader-options.test.js b/test/validate-loader-options.test.js index 299598db..e3c371dc 100644 --- a/test/validate-loader-options.test.js +++ b/test/validate-loader-options.test.js @@ -10,6 +10,10 @@ describe("validate options", () => { success: [true, false], failure: [1], }, + defaultExport: { + success: [true, false], + failure: [1], + }, unknown: { success: [], failure: [1, true, false, "test", /test/, [], {}, { foo: "bar" }], diff --git a/types/index.d.ts b/types/index.d.ts index 0765eb15..42151007 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -148,6 +148,7 @@ type PluginOptions = { * @property {boolean} [emit] * @property {boolean} [esModule] * @property {string} [layer] + * @property {boolean} [defaultExport] */ /** * @typedef {Object} PluginOptions @@ -200,6 +201,7 @@ type LoaderOptions = { emit?: boolean | undefined; esModule?: boolean | undefined; layer?: string | undefined; + defaultExport?: boolean | undefined; }; type NormalizedPluginOptions = { filename: Required["output"]["filename"]; From 657af90ab28419eacc1b4873541011f353da01ed Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Tue, 2 Apr 2024 20:56:58 +0300 Subject: [PATCH 5/6] test: fix --- README.md | 2 +- test/__snapshots__/validate-loader-options.test.js.snap | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 461e7b1e..42c3e674 100644 --- a/README.md +++ b/README.md @@ -562,7 +562,7 @@ Default: `false` > **Note** > -> This options will work only when you set `namedExport` to `true` in `css-loader` +> This option will work only when you set `namedExport` to `true` in `css-loader` By default, `mini-css-extract-plugin` generates JS modules based on the `esModule` and `namedExport` options in `css-loader`. Using the `esModule` and `namedExport` options will allow you to better optimize your code. diff --git a/test/__snapshots__/validate-loader-options.test.js.snap b/test/__snapshots__/validate-loader-options.test.js.snap index ed9f18c3..5f9bdadd 100644 --- a/test/__snapshots__/validate-loader-options.test.js.snap +++ b/test/__snapshots__/validate-loader-options.test.js.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`validate options should throw an error on the "defaultExport" option with "1" value 1`] = ` +"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema. + - options.defaultExport should be a boolean. + -> Duplicate the named export with CSS modules locals to the default export (only when \`esModules: true\` for css-loader). + -> Read more at https://github.com/webpack-contrib/mini-css-extract-plugin#defaultexports" +`; + exports[`validate options should throw an error on the "esModule" option with "1" value 1`] = ` "Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema. - options.esModule should be a boolean. From ddb84c682b821ed949b5710d1fea1ed2722151cd Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Tue, 2 Apr 2024 21:07:57 +0300 Subject: [PATCH 6/6] test: added --- .../expected/main.js | 4 +- .../expected/main.js | 9 +- .../expected/main.css | 12 +++ .../expected/main.js | 93 +++++++++++++++++++ .../es-named-and-default-export/index.js | 8 ++ .../es-named-and-default-export/style.css | 11 +++ .../webpack.config.js | 36 +++++++ .../expected/main.mjs | 4 +- .../es-named-export-as-is/expected/main.js | 4 +- .../expected/main.mjs | 4 +- test/cases/es-named-export/expected/main.js | 4 +- .../expected/main.js | 4 +- 12 files changed, 168 insertions(+), 25 deletions(-) create mode 100644 test/cases/es-named-and-default-export/expected/main.css create mode 100644 test/cases/es-named-and-default-export/expected/main.js create mode 100644 test/cases/es-named-and-default-export/index.js create mode 100644 test/cases/es-named-and-default-export/style.css create mode 100644 test/cases/es-named-and-default-export/webpack.config.js diff --git a/test/cases/custom-loader-with-functional-exports/expected/main.js b/test/cases/custom-loader-with-functional-exports/expected/main.js index 8648479e..e3e3d2ff 100644 --- a/test/cases/custom-loader-with-functional-exports/expected/main.js +++ b/test/cases/custom-loader-with-functional-exports/expected/main.js @@ -8,14 +8,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ cnA: () => (/* binding */ _1), -/* harmony export */ cnB: () => (/* binding */ _2), -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ cnB: () => (/* binding */ _2) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = () => "class-name-a"; var _2 = () => "class-name-b"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "cnA": _1, "cnB": _2 }); /***/ }) diff --git a/test/cases/es-module-concatenation-modules/expected/main.js b/test/cases/es-module-concatenation-modules/expected/main.js index 279943a9..918474db 100644 --- a/test/cases/es-module-concatenation-modules/expected/main.js +++ b/test/cases/es-module-concatenation-modules/expected/main.js @@ -48,16 +48,14 @@ __webpack_require__.d(__webpack_exports__, { var a_namespaceObject = {}; __webpack_require__.r(a_namespaceObject); __webpack_require__.d(a_namespaceObject, { - a: () => (_1), - "default": () => (a) + a: () => (_1) }); // NAMESPACE OBJECT: ./b.css var b_namespaceObject = {}; __webpack_require__.r(b_namespaceObject); __webpack_require__.d(b_namespaceObject, { - b: () => (b_1), - "default": () => (b) + b: () => (b_1) }); // NAMESPACE OBJECT: ./index.js @@ -73,19 +71,16 @@ __webpack_require__.d(index_namespaceObject, { // extracted by mini-css-extract-plugin var _1 = "foo__a"; -/* harmony default export */ const a = ({ "a": _1 }); ;// CONCATENATED MODULE: ./b.css // extracted by mini-css-extract-plugin var b_1 = "foo__b"; -/* harmony default export */ const b = ({ "b": b_1 }); ;// CONCATENATED MODULE: ./c.css // extracted by mini-css-extract-plugin var c_1 = "foo__c"; -/* harmony default export */ const c = ({ "c": c_1 }); ;// CONCATENATED MODULE: ./index.js /* eslint-disable import/no-namespace */ diff --git a/test/cases/es-named-and-default-export/expected/main.css b/test/cases/es-named-and-default-export/expected/main.css new file mode 100644 index 00000000..aa9b1569 --- /dev/null +++ b/test/cases/es-named-and-default-export/expected/main.css @@ -0,0 +1,12 @@ +.foo__style__a-class { + background: red; +} + +.foo__style__b__class { + color: green; +} + +.foo__style__cClass { + color: blue; +} + diff --git a/test/cases/es-named-and-default-export/expected/main.js b/test/cases/es-named-and-default-export/expected/main.js new file mode 100644 index 00000000..693ca53e --- /dev/null +++ b/test/cases/es-named-and-default-export/expected/main.js @@ -0,0 +1,93 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ([ +/* 0 */, +/* 1 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "a-class": () => (/* binding */ _1), +/* harmony export */ b__class: () => (/* binding */ _2), +/* harmony export */ cClass: () => (/* binding */ _3), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +// extracted by mini-css-extract-plugin +var _1 = "foo__style__a-class"; +var _2 = "foo__style__b__class"; +var _3 = "foo__style__cClass"; + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); + + +/***/ }) +/******/ ]); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); + + +// eslint-disable-next-line no-console +console.log({ css: _style_css__WEBPACK_IMPORTED_MODULE_0__["default"], aClass: _style_css__WEBPACK_IMPORTED_MODULE_0__["a-class"], bClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.b__class, cClass: _style_css__WEBPACK_IMPORTED_MODULE_0__.cClass }); + +})(); + +/******/ })() +; \ No newline at end of file diff --git a/test/cases/es-named-and-default-export/index.js b/test/cases/es-named-and-default-export/index.js new file mode 100644 index 00000000..aa114287 --- /dev/null +++ b/test/cases/es-named-and-default-export/index.js @@ -0,0 +1,8 @@ +import css, { + "a-class" as aClass, + "b__class" as bClass, + cClass, +} from "./style.css"; + +// eslint-disable-next-line no-console +console.log({ css, aClass, bClass, cClass }); diff --git a/test/cases/es-named-and-default-export/style.css b/test/cases/es-named-and-default-export/style.css new file mode 100644 index 00000000..a9085408 --- /dev/null +++ b/test/cases/es-named-and-default-export/style.css @@ -0,0 +1,11 @@ +.a-class { + background: red; +} + +.b__class { + color: green; +} + +.cClass { + color: blue; +} diff --git a/test/cases/es-named-and-default-export/webpack.config.js b/test/cases/es-named-and-default-export/webpack.config.js new file mode 100644 index 00000000..d7d364b3 --- /dev/null +++ b/test/cases/es-named-and-default-export/webpack.config.js @@ -0,0 +1,36 @@ +import Self from "../../../src"; + +module.exports = { + entry: "./index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [ + { + loader: Self.loader, + options: { + defaultExport: true, + }, + }, + { + loader: "css-loader", + options: { + esModule: true, + modules: { + namedExport: true, + exportLocalsConvention: "asIs", + localIdentName: "foo__[name]__[local]", + }, + }, + }, + ], + }, + ], + }, + plugins: [ + new Self({ + filename: "[name].css", + }), + ], +}; diff --git a/test/cases/es-named-export-as-is-output-module/expected/main.mjs b/test/cases/es-named-export-as-is-output-module/expected/main.mjs index b1a9cd9c..dd030372 100644 --- a/test/cases/es-named-export-as-is-output-module/expected/main.mjs +++ b/test/cases/es-named-export-as-is-output-module/expected/main.mjs @@ -7,15 +7,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "a-class": () => (/* binding */ _1), /* harmony export */ b__class: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3), -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ cClass: () => (/* binding */ _3) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "Xh041yLR4iCP4RGjge50"; var _2 = "NMuRsxoDwvW8BhSXhFAY"; var _3 = "ayWIv09rPsAqE2JznIsI"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/es-named-export-as-is/expected/main.js b/test/cases/es-named-export-as-is/expected/main.js index 260d0610..a17ffa68 100644 --- a/test/cases/es-named-export-as-is/expected/main.js +++ b/test/cases/es-named-export-as-is/expected/main.js @@ -9,15 +9,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "a-class": () => (/* binding */ _1), /* harmony export */ b__class: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3), -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ cClass: () => (/* binding */ _3) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "Xh041yLR4iCP4RGjge50"; var _2 = "NMuRsxoDwvW8BhSXhFAY"; var _3 = "ayWIv09rPsAqE2JznIsI"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/es-named-export-output-module/expected/main.mjs b/test/cases/es-named-export-output-module/expected/main.mjs index ef9a39c9..eea1b171 100644 --- a/test/cases/es-named-export-output-module/expected/main.mjs +++ b/test/cases/es-named-export-output-module/expected/main.mjs @@ -7,15 +7,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ aClass: () => (/* binding */ _1), /* harmony export */ bClass: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3), -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ cClass: () => (/* binding */ _3) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "aClass": _1, "bClass": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/es-named-export/expected/main.js b/test/cases/es-named-export/expected/main.js index 4e92f660..08202354 100644 --- a/test/cases/es-named-export/expected/main.js +++ b/test/cases/es-named-export/expected/main.js @@ -9,15 +9,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "a-class": () => (/* binding */ _1), /* harmony export */ b__class: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3), -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ cClass: () => (/* binding */ _3) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 }); /***/ }) diff --git a/test/cases/export-only-locals-and-es-named-export/expected/main.js b/test/cases/export-only-locals-and-es-named-export/expected/main.js index ba7befb1..fc556c4f 100644 --- a/test/cases/export-only-locals-and-es-named-export/expected/main.js +++ b/test/cases/export-only-locals-and-es-named-export/expected/main.js @@ -9,15 +9,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ aClass: () => (/* binding */ _1), /* harmony export */ bClass: () => (/* binding */ _2), -/* harmony export */ cClass: () => (/* binding */ _3), -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ cClass: () => (/* binding */ _3) /* harmony export */ }); // extracted by mini-css-extract-plugin var _1 = "foo__style__a-class"; var _2 = "foo__style__b__class"; var _3 = "foo__style__cClass"; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "aClass": _1, "bClass": _2, "cClass": _3 }); /***/ })