Skip to content

Commit ae7850e

Browse files
feat: exportLocalsConvention option could be a function
1 parent cc0c1b4 commit ae7850e

11 files changed

+443
-12
lines changed

README.md

+55-1
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,9 @@ module.exports = {
10311031
};
10321032
```
10331033

1034+
To set a custom name for namedExport, can use [`exportLocalsConvention`](#exportLocalsConvention) option as a function.
1035+
Example below in the [`examples`](#examples) section.
1036+
10341037
##### `exportGlobals`
10351038

10361039
Type: `Boolean`
@@ -1060,11 +1063,13 @@ module.exports = {
10601063

10611064
##### `exportLocalsConvention`
10621065

1063-
Type: `String`
1066+
Type: `String|Function`
10641067
Default: based on the `modules.namedExport` option value, if `true` - `camelCaseOnly`, otherwise `asIs`
10651068

10661069
Style of exported class names.
10671070

1071+
###### `String`
1072+
10681073
By default, the exported JSON keys mirror the class names (i.e `asIs` value).
10691074

10701075
> ⚠ Only `camelCaseOnly` value allowed if you set the `namedExport` value to `true`.
@@ -1110,6 +1115,30 @@ module.exports = {
11101115
};
11111116
```
11121117

1118+
###### `Function`
1119+
1120+
**webpack.config.js**
1121+
1122+
```js
1123+
module.exports = {
1124+
module: {
1125+
rules: [
1126+
{
1127+
test: /\.css$/i,
1128+
loader: "css-loader",
1129+
options: {
1130+
modules: {
1131+
exportLocalsConvention: function (name) {
1132+
return name.replace(/-/g, "_");
1133+
},
1134+
},
1135+
},
1136+
},
1137+
],
1138+
},
1139+
};
1140+
```
1141+
11131142
##### `exportOnlyLocals`
11141143

11151144
Type: `Boolean`
@@ -1434,6 +1463,31 @@ module.exports = {
14341463
};
14351464
```
14361465

1466+
### Named export with custom export names
1467+
1468+
**webpack.config.js**
1469+
1470+
```js
1471+
module.exports = {
1472+
module: {
1473+
rules: [
1474+
{
1475+
test: /\.css$/i,
1476+
loader: "css-loader",
1477+
options: {
1478+
modules: {
1479+
namedExport: true,
1480+
exportLocalsConvention: function (name) {
1481+
return name.replace(/-/g, "_");
1482+
},
1483+
},
1484+
},
1485+
},
1486+
],
1487+
},
1488+
};
1489+
```
1490+
14371491
### Separating `Interoperable CSS`-only and `CSS Module` features
14381492

14391493
The following setup is an example of allowing `Interoperable CSS` features only (such as `:import` and `:export`) without using further `CSS Module` functionality by setting `mode` option for all files that do not match `*.module.scss` naming convention. This is for reference as having `ICSS` features applied to all files was default `css-loader` behavior before v4.

src/index.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,17 @@ export default async function loader(content, map, meta) {
216216
}
217217

218218
const importCode = getImportCode(imports, options);
219-
const moduleCode = getModuleCode(result, api, replacements, options, this);
219+
220+
let moduleCode;
221+
222+
try {
223+
moduleCode = getModuleCode(result, api, replacements, options, this);
224+
} catch (error) {
225+
callback(error);
226+
227+
return;
228+
}
229+
220230
const exportCode = getExportCode(
221231
exports,
222232
replacements,

src/options.json

+13-6
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,19 @@
145145
"exportLocalsConvention": {
146146
"description": "Style of exported classnames.",
147147
"link": "https://github.com/webpack-contrib/css-loader#localsconvention",
148-
"enum": [
149-
"asIs",
150-
"camelCase",
151-
"camelCaseOnly",
152-
"dashes",
153-
"dashesOnly"
148+
"anyOf": [
149+
{
150+
"enum": [
151+
"asIs",
152+
"camelCase",
153+
"camelCaseOnly",
154+
"dashes",
155+
"dashesOnly"
156+
]
157+
},
158+
{
159+
"instanceof": "Function"
160+
}
154161
]
155162
},
156163
"exportOnlyLocals": {

src/utils.js

+15
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,10 @@ function getFilter(filter, resourcePath) {
485485
}
486486

487487
function getValidLocalName(localName, exportLocalsConvention) {
488+
if (typeof exportLocalsConvention === "function") {
489+
return exportLocalsConvention(localName);
490+
}
491+
488492
if (exportLocalsConvention === "dashesOnly") {
489493
return dashesCamelCase(localName);
490494
}
@@ -588,6 +592,7 @@ function getModulesOptions(rawOptions, loaderContext) {
588592
}
589593

590594
if (
595+
typeof modulesOptions.exportLocalsConvention === "string" &&
591596
modulesOptions.exportLocalsConvention !== "camelCaseOnly" &&
592597
modulesOptions.exportLocalsConvention !== "dashesOnly"
593598
) {
@@ -970,6 +975,16 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
970975
};
971976

972977
for (const { name, value } of exports) {
978+
if (typeof options.modules.exportLocalsConvention === "function") {
979+
addExportToLocalsCode(
980+
options.modules.exportLocalsConvention(name),
981+
value
982+
);
983+
984+
// eslint-disable-next-line no-continue
985+
continue;
986+
}
987+
973988
switch (options.modules.exportLocalsConvention) {
974989
case "camelCase": {
975990
addExportToLocalsCode(name, value);

0 commit comments

Comments
 (0)