Skip to content

Commit 74ae781

Browse files
authored
fix: added the defaultExport option to generate default and named export together (#1084)
1 parent 92c7eb3 commit 74ae781

File tree

12 files changed

+252
-9
lines changed

12 files changed

+252
-9
lines changed

README.md

+55
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ module.exports = {
407407
- **[`publicPath`](#publicPath)**
408408
- **[`emit`](#emit)**
409409
- **[`esModule`](#esModule)**
410+
- **[`defaultExport`](#defaultExport)**
410411

411412
#### `publicPath`
412413

@@ -549,6 +550,60 @@ module.exports = {
549550
};
550551
```
551552

553+
#### `defaultExport`
554+
555+
Type:
556+
557+
```ts
558+
type defaultExport = boolean;
559+
```
560+
561+
Default: `false`
562+
563+
> **Note**
564+
>
565+
> This option will work only when you set `namedExport` to `true` in `css-loader`
566+
567+
By default, `mini-css-extract-plugin` generates JS modules based on the `esModule` and `namedExport` options in `css-loader`.
568+
Using the `esModule` and `namedExport` options will allow you to better optimize your code.
569+
If you set `esModule: true` and `namedExport: true` for `css-loader` `mini-css-extract-plugin` will generate **only** a named export.
570+
Our official recommendation is to use only named export for better future compatibility.
571+
But for some applications, it is not easy to quickly rewrite the code from the default export to a named export.
572+
573+
In case you need both default and named exports, you can enable this option:
574+
575+
**webpack.config.js**
576+
577+
```js
578+
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
579+
580+
module.exports = {
581+
plugins: [new MiniCssExtractPlugin()],
582+
module: {
583+
rules: [
584+
{
585+
test: /\.css$/i,
586+
use: [
587+
{
588+
loader: MiniCssExtractPlugin.loader,
589+
options: {
590+
defaultExport: true,
591+
},
592+
},
593+
{
594+
loader: "css-loader",
595+
esModule: true,
596+
modules: {
597+
namedExport: true,
598+
},
599+
},
600+
],
601+
},
602+
],
603+
},
604+
};
605+
```
606+
552607
## Examples
553608

554609
### Recommended

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const {
3737
* @property {boolean} [emit]
3838
* @property {boolean} [esModule]
3939
* @property {string} [layer]
40+
* @property {boolean} [defaultExport]
4041
*/
4142

4243
/**

src/loader-options.json

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
},
2828
"layer": {
2929
"type": "string"
30+
},
31+
"defaultExport": {
32+
"type": "boolean",
33+
"description": "Duplicate the named export with CSS modules locals to the default export (only when `esModules: true` for css-loader).",
34+
"link": "https://github.com/webpack-contrib/mini-css-extract-plugin#defaultexports"
3035
}
3136
}
3237
}

src/loader.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,16 @@ function pitch(request) {
272272
.map(([id, key]) => `${id} as ${JSON.stringify(key)}`)
273273
.join(", ")} }`;
274274

275-
return `${localsString}\n${exportsString}\n`;
275+
const defaultExport =
276+
typeof options.defaultExport !== "undefined"
277+
? options.defaultExport
278+
: false;
279+
280+
return defaultExport
281+
? `${localsString}\n${exportsString}\nexport default { ${identifiers
282+
.map(([id, key]) => `${JSON.stringify(key)}: ${id}`)
283+
.join(", ")} }\n`
284+
: `${localsString}\n${exportsString}\n`;
276285
}
277286

278287
return `\n${

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

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`validate options should throw an error on the "defaultExport" option with "1" value 1`] = `
4+
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
5+
- options.defaultExport should be a boolean.
6+
-> Duplicate the named export with CSS modules locals to the default export (only when \`esModules: true\` for css-loader).
7+
-> Read more at https://github.com/webpack-contrib/mini-css-extract-plugin#defaultexports"
8+
`;
9+
310
exports[`validate options should throw an error on the "esModule" option with "1" value 1`] = `
411
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
512
- options.esModule should be a boolean.
@@ -21,47 +28,47 @@ exports[`validate options should throw an error on the "publicPath" option with
2128
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
2229
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
2330
- options has an unknown property 'unknown'. These properties are valid:
24-
object { publicPath?, emit?, esModule?, layer? }"
31+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
2532
`;
2633
2734
exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = `
2835
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
2936
- options has an unknown property 'unknown'. These properties are valid:
30-
object { publicPath?, emit?, esModule?, layer? }"
37+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
3138
`;
3239
3340
exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = `
3441
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
3542
- options has an unknown property 'unknown'. These properties are valid:
36-
object { publicPath?, emit?, esModule?, layer? }"
43+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
3744
`;
3845
3946
exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = `
4047
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
4148
- options has an unknown property 'unknown'. These properties are valid:
42-
object { publicPath?, emit?, esModule?, layer? }"
49+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
4350
`;
4451
4552
exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = `
4653
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
4754
- options has an unknown property 'unknown'. These properties are valid:
48-
object { publicPath?, emit?, esModule?, layer? }"
55+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
4956
`;
5057
5158
exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = `
5259
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
5360
- options has an unknown property 'unknown'. These properties are valid:
54-
object { publicPath?, emit?, esModule?, layer? }"
61+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
5562
`;
5663
5764
exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = `
5865
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
5966
- options has an unknown property 'unknown'. These properties are valid:
60-
object { publicPath?, emit?, esModule?, layer? }"
67+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
6168
`;
6269
6370
exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = `
6471
"Invalid options object. Mini CSS Extract Plugin Loader has been initialized using an options object that does not match the API schema.
6572
- options has an unknown property 'unknown'. These properties are valid:
66-
object { publicPath?, emit?, esModule?, layer? }"
73+
object { publicPath?, emit?, esModule?, layer?, defaultExport? }"
6774
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.foo__style__a-class {
2+
background: red;
3+
}
4+
5+
.foo__style__b__class {
6+
color: green;
7+
}
8+
9+
.foo__style__cClass {
10+
color: blue;
11+
}
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/******/ (() => { // webpackBootstrap
2+
/******/ "use strict";
3+
/******/ var __webpack_modules__ = ([
4+
/* 0 */,
5+
/* 1 */
6+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7+
8+
__webpack_require__.r(__webpack_exports__);
9+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
10+
/* harmony export */ "a-class": () => (/* binding */ _1),
11+
/* harmony export */ b__class: () => (/* binding */ _2),
12+
/* harmony export */ cClass: () => (/* binding */ _3),
13+
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
14+
/* harmony export */ });
15+
// extracted by mini-css-extract-plugin
16+
var _1 = "foo__style__a-class";
17+
var _2 = "foo__style__b__class";
18+
var _3 = "foo__style__cClass";
19+
20+
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ "a-class": _1, "b__class": _2, "cClass": _3 });
21+
22+
23+
/***/ })
24+
/******/ ]);
25+
/************************************************************************/
26+
/******/ // The module cache
27+
/******/ var __webpack_module_cache__ = {};
28+
/******/
29+
/******/ // The require function
30+
/******/ function __webpack_require__(moduleId) {
31+
/******/ // Check if module is in cache
32+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
33+
/******/ if (cachedModule !== undefined) {
34+
/******/ return cachedModule.exports;
35+
/******/ }
36+
/******/ // Create a new module (and put it into the cache)
37+
/******/ var module = __webpack_module_cache__[moduleId] = {
38+
/******/ // no module.id needed
39+
/******/ // no module.loaded needed
40+
/******/ exports: {}
41+
/******/ };
42+
/******/
43+
/******/ // Execute the module function
44+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
45+
/******/
46+
/******/ // Return the exports of the module
47+
/******/ return module.exports;
48+
/******/ }
49+
/******/
50+
/************************************************************************/
51+
/******/ /* webpack/runtime/define property getters */
52+
/******/ (() => {
53+
/******/ // define getter functions for harmony exports
54+
/******/ __webpack_require__.d = (exports, definition) => {
55+
/******/ for(var key in definition) {
56+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
57+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
58+
/******/ }
59+
/******/ }
60+
/******/ };
61+
/******/ })();
62+
/******/
63+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
64+
/******/ (() => {
65+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
66+
/******/ })();
67+
/******/
68+
/******/ /* webpack/runtime/make namespace object */
69+
/******/ (() => {
70+
/******/ // define __esModule on exports
71+
/******/ __webpack_require__.r = (exports) => {
72+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
73+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
74+
/******/ }
75+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
76+
/******/ };
77+
/******/ })();
78+
/******/
79+
/************************************************************************/
80+
var __webpack_exports__ = {};
81+
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
82+
(() => {
83+
__webpack_require__.r(__webpack_exports__);
84+
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
85+
86+
87+
// eslint-disable-next-line no-console
88+
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 });
89+
90+
})();
91+
92+
/******/ })()
93+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import css, {
2+
"a-class" as aClass,
3+
"b__class" as bClass,
4+
cClass,
5+
} from "./style.css";
6+
7+
// eslint-disable-next-line no-console
8+
console.log({ css, aClass, bClass, cClass });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.a-class {
2+
background: red;
3+
}
4+
5+
.b__class {
6+
color: green;
7+
}
8+
9+
.cClass {
10+
color: blue;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Self from "../../../src";
2+
3+
module.exports = {
4+
entry: "./index.js",
5+
module: {
6+
rules: [
7+
{
8+
test: /\.css$/,
9+
use: [
10+
{
11+
loader: Self.loader,
12+
options: {
13+
defaultExport: true,
14+
},
15+
},
16+
{
17+
loader: "css-loader",
18+
options: {
19+
esModule: true,
20+
modules: {
21+
namedExport: true,
22+
exportLocalsConvention: "asIs",
23+
localIdentName: "foo__[name]__[local]",
24+
},
25+
},
26+
},
27+
],
28+
},
29+
],
30+
},
31+
plugins: [
32+
new Self({
33+
filename: "[name].css",
34+
}),
35+
],
36+
};

test/validate-loader-options.test.js

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ describe("validate options", () => {
1010
success: [true, false],
1111
failure: [1],
1212
},
13+
defaultExport: {
14+
success: [true, false],
15+
failure: [1],
16+
},
1317
unknown: {
1418
success: [],
1519
failure: [1, true, false, "test", /test/, [], {}, { foo: "bar" }],

types/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ type PluginOptions = {
148148
* @property {boolean} [emit]
149149
* @property {boolean} [esModule]
150150
* @property {string} [layer]
151+
* @property {boolean} [defaultExport]
151152
*/
152153
/**
153154
* @typedef {Object} PluginOptions
@@ -200,6 +201,7 @@ type LoaderOptions = {
200201
emit?: boolean | undefined;
201202
esModule?: boolean | undefined;
202203
layer?: string | undefined;
204+
defaultExport?: boolean | undefined;
203205
};
204206
type NormalizedPluginOptions = {
205207
filename: Required<Configuration>["output"]["filename"];

0 commit comments

Comments
 (0)