Skip to content

Commit 1ea4b7f

Browse files
feat: named export
1 parent ff4bfbe commit 1ea4b7f

File tree

13 files changed

+417
-12
lines changed

13 files changed

+417
-12
lines changed

.github/workflows/nodejs.yml

+1-5
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,8 @@ jobs:
5656
matrix:
5757
os: [ubuntu-latest, windows-latest, macos-latest]
5858
# css-loader doesn't support node@6
59-
node-version: [8.x, 10.x, 12.x, 14.x]
59+
node-version: [10.x, 12.x, 14.x]
6060
webpack-version: [latest, next]
61-
exclude:
62-
# Webpack 5 does not support node 6 and 8
63-
- node-version: 8.x
64-
webpack-version: next
6561

6662
runs-on: ${{ matrix.os }}
6763

README.md

+79
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,85 @@ module.exports = {
188188
};
189189
```
190190

191+
### `modules`
192+
193+
Type: `Object`
194+
Default: `undefined`
195+
196+
Configuration CSS Modules.
197+
198+
#### `namedExport`
199+
200+
Type: `Boolean`
201+
Default: `false`
202+
203+
Enables/disables ES modules named export for locals.
204+
205+
> ⚠ Names of locals are converted to `camelCase`.
206+
207+
> ⚠ It is not allowed to use JavaScript reserved words in css class names.
208+
209+
> ⚠ Options `esModule` and `modules.namedExport` in `css-loader` and `MiniCssExtractPlugin.loader` should be enabled.
210+
211+
**styles.css**
212+
213+
```css
214+
.foo-baz {
215+
color: red;
216+
}
217+
.bar {
218+
color: blue;
219+
}
220+
```
221+
222+
**index.js**
223+
224+
```js
225+
import { fooBaz, bar } from './styles.css';
226+
227+
console.log(fooBaz, bar);
228+
```
229+
230+
You can enable a ES module named export using:
231+
232+
**webpack.config.js**
233+
234+
```js
235+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
236+
237+
module.exports = {
238+
plugins: [new MiniCssExtractPlugin()],
239+
module: {
240+
rules: [
241+
{
242+
test: /\.css$/,
243+
use: [
244+
{
245+
loader: MiniCssExtractPlugin.loader,
246+
options: {
247+
esModule: true,
248+
modules: {
249+
namedExport: true,
250+
},
251+
},
252+
},
253+
{
254+
loader: 'css-loader',
255+
options: {
256+
esModule: true,
257+
modules: {
258+
namedExport: true,
259+
localIdentName: 'foo__[name]__[local]',
260+
},
261+
},
262+
},
263+
],
264+
},
265+
],
266+
},
267+
};
268+
```
269+
191270
## Examples
192271

193272
### Minimal example

src/loader-options.json

+10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@
2020
},
2121
"reloadAll": {
2222
"type": "boolean"
23+
},
24+
"modules": {
25+
"type": "object",
26+
"additionalProperties": false,
27+
"properties": {
28+
"namedExport": {
29+
"description": "Enables/disables ES modules named export for locals (https://webpack.js.org/plugins/mini-css-extract-plugin/#namedexport).",
30+
"type": "boolean"
31+
}
32+
}
2333
}
2434
}
2535
}

src/loader.js

+26-7
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,29 @@ export function pitch(request) {
206206
}
207207

208208
let locals;
209+
let result = '';
209210

210211
try {
211212
let dependencies;
212213
let exports = evalModuleCode(this, source, request);
214+
215+
if (
216+
options.modules &&
217+
options.modules.namedExport &&
218+
// eslint-disable-next-line no-underscore-dangle
219+
exports.__esModule
220+
) {
221+
Object.keys(exports).forEach((key) => {
222+
if (key !== 'default') {
223+
result += `\nexport const ${key} = "${exports[key]}";`;
224+
}
225+
});
226+
}
227+
213228
// eslint-disable-next-line no-underscore-dangle
214229
exports = exports.__esModule ? exports.default : exports;
215230
locals = exports && exports.locals;
231+
216232
if (!Array.isArray(exports)) {
217233
dependencies = [[null, exports]];
218234
} else {
@@ -235,13 +251,16 @@ export function pitch(request) {
235251

236252
const esModule =
237253
typeof options.esModule !== 'undefined' ? options.esModule : false;
238-
const result = locals
239-
? `\n${esModule ? 'export default' : 'module.exports ='} ${JSON.stringify(
240-
locals
241-
)};`
242-
: esModule
243-
? `\nexport {};`
244-
: '';
254+
255+
if (!result) {
256+
result += locals
257+
? `\n${
258+
esModule ? 'export default' : 'module.exports ='
259+
} ${JSON.stringify(locals)};`
260+
: esModule
261+
? `\nexport {};`
262+
: '';
263+
}
245264

246265
let resultSource = `// extracted by ${pluginName}`;
247266

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

+14
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ options.hmr should be boolean
1414
"
1515
`;
1616

17+
exports[`validate options should throw an error on the "modules" option with "{"namedExport":"false"}" value 1`] = `
18+
"Mini CSS Extract Plugin Loader Invalid Options
19+
20+
options.modules.namedExport should be boolean
21+
"
22+
`;
23+
24+
exports[`validate options should throw an error on the "modules" option with "true" value 1`] = `
25+
"Mini CSS Extract Plugin Loader Invalid Options
26+
27+
options.modules should be object
28+
"
29+
`;
30+
1731
exports[`validate options should throw an error on the "publicPath" option with "true" value 1`] = `
1832
"Mini CSS Extract Plugin Loader Invalid Options
1933
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,115 @@
1+
/******/ (function(modules) { // webpackBootstrap
2+
/******/ // The module cache
3+
/******/ var installedModules = {};
4+
/******/
5+
/******/ // The require function
6+
/******/ function __webpack_require__(moduleId) {
7+
/******/
8+
/******/ // Check if module is in cache
9+
/******/ if(installedModules[moduleId]) {
10+
/******/ return installedModules[moduleId].exports;
11+
/******/ }
12+
/******/ // Create a new module (and put it into the cache)
13+
/******/ var module = installedModules[moduleId] = {
14+
/******/ i: moduleId,
15+
/******/ l: false,
16+
/******/ exports: {}
17+
/******/ };
18+
/******/
19+
/******/ // Execute the module function
20+
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21+
/******/
22+
/******/ // Flag the module as loaded
23+
/******/ module.l = true;
24+
/******/
25+
/******/ // Return the exports of the module
26+
/******/ return module.exports;
27+
/******/ }
28+
/******/
29+
/******/
30+
/******/ // expose the modules object (__webpack_modules__)
31+
/******/ __webpack_require__.m = modules;
32+
/******/
33+
/******/ // expose the module cache
34+
/******/ __webpack_require__.c = installedModules;
35+
/******/
36+
/******/ // define getter function for harmony exports
37+
/******/ __webpack_require__.d = function(exports, name, getter) {
38+
/******/ if(!__webpack_require__.o(exports, name)) {
39+
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
40+
/******/ }
41+
/******/ };
42+
/******/
43+
/******/ // define __esModule on exports
44+
/******/ __webpack_require__.r = function(exports) {
45+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
46+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
47+
/******/ }
48+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
49+
/******/ };
50+
/******/
51+
/******/ // create a fake namespace object
52+
/******/ // mode & 1: value is a module id, require it
53+
/******/ // mode & 2: merge all properties of value into the ns
54+
/******/ // mode & 4: return value when already ns object
55+
/******/ // mode & 8|1: behave like require
56+
/******/ __webpack_require__.t = function(value, mode) {
57+
/******/ if(mode & 1) value = __webpack_require__(value);
58+
/******/ if(mode & 8) return value;
59+
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
60+
/******/ var ns = Object.create(null);
61+
/******/ __webpack_require__.r(ns);
62+
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
63+
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
64+
/******/ return ns;
65+
/******/ };
66+
/******/
67+
/******/ // getDefaultExport function for compatibility with non-harmony modules
68+
/******/ __webpack_require__.n = function(module) {
69+
/******/ var getter = module && module.__esModule ?
70+
/******/ function getDefault() { return module['default']; } :
71+
/******/ function getModuleExports() { return module; };
72+
/******/ __webpack_require__.d(getter, 'a', getter);
73+
/******/ return getter;
74+
/******/ };
75+
/******/
76+
/******/ // Object.prototype.hasOwnProperty.call
77+
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
78+
/******/
79+
/******/ // __webpack_public_path__
80+
/******/ __webpack_require__.p = "";
81+
/******/
82+
/******/
83+
/******/ // Load entry module and return exports
84+
/******/ return __webpack_require__(__webpack_require__.s = 0);
85+
/******/ })
86+
/************************************************************************/
87+
/******/ ([
88+
/* 0 */
89+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
90+
91+
"use strict";
92+
__webpack_require__.r(__webpack_exports__);
93+
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
94+
95+
96+
// eslint-disable-next-line no-console
97+
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"] });
98+
99+
100+
/***/ }),
101+
/* 1 */
102+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
103+
104+
"use strict";
105+
__webpack_require__.r(__webpack_exports__);
106+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "aClass", function() { return aClass; });
107+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bClass", function() { return bClass; });
108+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "cClass", function() { return cClass; });
109+
// extracted by mini-css-extract-plugin
110+
const aClass = "foo__style__a-class";
111+
const bClass = "foo__style__b__class";
112+
const cClass = "foo__style__cClass";
113+
114+
/***/ })
115+
/******/ ]);
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+

0 commit comments

Comments
 (0)