Skip to content

Commit a990f79

Browse files
feat: exportLocalsConvention option could return array with names
1 parent ae7850e commit a990f79

File tree

4 files changed

+224
-19
lines changed

4 files changed

+224
-19
lines changed

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,34 @@ module.exports = {
11391139
};
11401140
```
11411141

1142+
**webpack.config.js**
1143+
1144+
```js
1145+
module.exports = {
1146+
module: {
1147+
rules: [
1148+
{
1149+
test: /\.css$/i,
1150+
loader: "css-loader",
1151+
options: {
1152+
modules: {
1153+
exportLocalsConvention: function (name) {
1154+
return [
1155+
name.replace(/-/g, "_"),
1156+
// dashesCamelCase
1157+
name.replace(/-+(\w)/g, (match, firstLetter) =>
1158+
firstLetter.toUpperCase()
1159+
),
1160+
];
1161+
},
1162+
},
1163+
},
1164+
},
1165+
],
1166+
},
1167+
};
1168+
```
1169+
11421170
##### `exportOnlyLocals`
11431171

11441172
Type: `Boolean`

src/utils.js

+19-19
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,9 @@ function getFilter(filter, resourcePath) {
486486

487487
function getValidLocalName(localName, exportLocalsConvention) {
488488
if (typeof exportLocalsConvention === "function") {
489-
return exportLocalsConvention(localName);
489+
const result = exportLocalsConvention(localName);
490+
491+
return Array.isArray(result) ? result[0] : result;
490492
}
491493

492494
if (exportLocalsConvention === "dashesOnly") {
@@ -962,15 +964,21 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
962964

963965
let localsCode = "";
964966

965-
const addExportToLocalsCode = (name, value) => {
966-
if (options.modules.namedExport) {
967-
localsCode += `export var ${name} = ${JSON.stringify(value)};\n`;
968-
} else {
969-
if (localsCode) {
970-
localsCode += `,\n`;
971-
}
967+
const addExportToLocalsCode = (names, value) => {
968+
const normalizedNames = Array.isArray(names)
969+
? new Set(names)
970+
: new Set([names]);
972971

973-
localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`;
972+
for (const name of normalizedNames) {
973+
if (options.modules.namedExport) {
974+
localsCode += `export var ${name} = ${JSON.stringify(value)};\n`;
975+
} else {
976+
if (localsCode) {
977+
localsCode += `,\n`;
978+
}
979+
980+
localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`;
981+
}
974982
}
975983
};
976984

@@ -987,27 +995,19 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
987995

988996
switch (options.modules.exportLocalsConvention) {
989997
case "camelCase": {
990-
addExportToLocalsCode(name, value);
991-
992998
const modifiedName = camelCase(name);
993999

994-
if (modifiedName !== name) {
995-
addExportToLocalsCode(modifiedName, value);
996-
}
1000+
addExportToLocalsCode([name, modifiedName], value);
9971001
break;
9981002
}
9991003
case "camelCaseOnly": {
10001004
addExportToLocalsCode(camelCase(name), value);
10011005
break;
10021006
}
10031007
case "dashes": {
1004-
addExportToLocalsCode(name, value);
1005-
10061008
const modifiedName = dashesCamelCase(name);
10071009

1008-
if (modifiedName !== name) {
1009-
addExportToLocalsCode(modifiedName, value);
1010-
}
1010+
addExportToLocalsCode([name, modifiedName], value);
10111011
break;
10121012
}
10131013
case "dashesOnly": {

test/__snapshots__/modules-option.test.js.snap

+128
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,64 @@ Array [
25712571

25722572
exports[`"modules" option should work and respect the "context" option: warnings 1`] = `Array []`;
25732573

2574+
exports[`"modules" option should work and respect the "exportLocalsConvention" option with the "function" type and returns array names: errors 1`] = `Array []`;
2575+
2576+
exports[`"modules" option should work and respect the "exportLocalsConvention" option with the "function" type and returns array names: module 1`] = `
2577+
"// Imports
2578+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\";
2579+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
2580+
// Module
2581+
___CSS_LOADER_EXPORT___.push([module.id, \\".eFSx39d7lf2DbavLOZEH {\\\\n color: blue;\\\\n}\\\\n\\\\n._XcV1pTGsk1DDypSCcav {\\\\n color: blue;\\\\n}\\\\n\\\\n._JxN_SGMxSzstCVbNTUy {\\\\n color: red;\\\\n}\\\\n\\\\na {\\\\n color: yellow;\\\\n}\\\\n\\\\n._krAefTYwrSG1l87lmV3 {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]);
2582+
// Exports
2583+
___CSS_LOADER_EXPORT___.locals = {
2584+
\\"foo_TEST_1\\": \\"bar\\",
2585+
\\"foo_TEST_3\\": \\"bar\\",
2586+
\\"my_btn_info_is_disabled_TEST_1\\": \\"value\\",
2587+
\\"my_btn_info_is_disabled_TEST_3\\": \\"value\\",
2588+
\\"btn_info_is_disabled_TEST_1\\": \\"eFSx39d7lf2DbavLOZEH\\",
2589+
\\"btn_info_is_disabled_TEST_3\\": \\"eFSx39d7lf2DbavLOZEH\\",
2590+
\\"btn__info_is_disabled_1_TEST_1\\": \\"_XcV1pTGsk1DDypSCcav\\",
2591+
\\"btn__info_is_disabled_1_TEST_3\\": \\"_XcV1pTGsk1DDypSCcav\\",
2592+
\\"simple_TEST_1\\": \\"_JxN_SGMxSzstCVbNTUy\\",
2593+
\\"simple_TEST_3\\": \\"_JxN_SGMxSzstCVbNTUy\\",
2594+
\\"foo_bar_TEST_1\\": \\"_krAefTYwrSG1l87lmV3\\",
2595+
\\"foo_bar_TEST_3\\": \\"_krAefTYwrSG1l87lmV3\\"
2596+
};
2597+
export default ___CSS_LOADER_EXPORT___;
2598+
"
2599+
`;
2600+
2601+
exports[`"modules" option should work and respect the "exportLocalsConvention" option with the "function" type and returns array names: result 1`] = `
2602+
Array [
2603+
Array [
2604+
"./modules/localsConvention/localsConvention.css",
2605+
".eFSx39d7lf2DbavLOZEH {
2606+
color: blue;
2607+
}
2608+
2609+
._XcV1pTGsk1DDypSCcav {
2610+
color: blue;
2611+
}
2612+
2613+
._JxN_SGMxSzstCVbNTUy {
2614+
color: red;
2615+
}
2616+
2617+
a {
2618+
color: yellow;
2619+
}
2620+
2621+
._krAefTYwrSG1l87lmV3 {
2622+
color: red;
2623+
}
2624+
",
2625+
"",
2626+
],
2627+
]
2628+
`;
2629+
2630+
exports[`"modules" option should work and respect the "exportLocalsConvention" option with the "function" type and returns array names: warnings 1`] = `Array []`;
2631+
25742632
exports[`"modules" option should work and respect the "exportLocalsConvention" option with the "function" type: errors 1`] = `Array []`;
25752633

25762634
exports[`"modules" option should work and respect the "exportLocalsConvention" option with the "function" type: module 1`] = `
@@ -13552,6 +13610,76 @@ Array [
1355213610

1355313611
exports[`"modules" option should work with case \`values-10\` (\`modules\` value is \`true)\`: warnings 1`] = `Array []`;
1355413612

13613+
exports[`"modules" option should work with composes when the "exportLocalsConvention" is function and return array names: errors 1`] = `Array []`;
13614+
13615+
exports[`"modules" option should work with composes when the "exportLocalsConvention" is function and return array names: module 1`] = `
13616+
"// Imports
13617+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
13618+
import ___CSS_LOADER_ICSS_IMPORT_0___, * as ___CSS_LOADER_ICSS_IMPORT_0____NAMED___ from \\"-!../../../../../src/index.js??ruleSet[1].rules[0].use[0]!./values.css\\";
13619+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
13620+
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
13621+
// Module
13622+
___CSS_LOADER_EXPORT___.push([module.id, \\".hrlxzfp4noajRWPVp_UC {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST_1\\"] + \\";\\\\n}\\\\n\\\\n.Wr3wRBz8YFj3jWOISgia {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"s_white_TEST_1\\"] + \\";\\\\n}\\\\n\\\\n.N3CMKfMxK4wVelMiMLmQ {\\\\n display: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"m_small_TEST_1\\"] + \\";\\\\n}\\\\n\\\\n.pgrezI_6B1TIw29jJdO0 {\\\\n width: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST_1\\"] + \\";\\\\n}\\\\n\\\\n._wneqlVHVVNWG_hbM3Ti {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_other_other_TEST_1\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
13623+
// Exports
13624+
export var v_def_TEST_1 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST_1\\"] + \\"\\";
13625+
export var v_def_TEST_3 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST_1\\"] + \\"\\";
13626+
export var v_other_other_TEST_1 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_other_other_TEST_1\\"] + \\"\\";
13627+
export var v_other_other_TEST_3 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_other_other_TEST_1\\"] + \\"\\";
13628+
export var s_white_TEST_1 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"s_white_TEST_1\\"] + \\"\\";
13629+
export var s_white_TEST_3 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"s_white_TEST_1\\"] + \\"\\";
13630+
export var m_small_TEST_1 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"m_small_TEST_1\\"] + \\"\\";
13631+
export var m_small_TEST_3 = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"m_small_TEST_1\\"] + \\"\\";
13632+
export var ghi_TEST_1 = \\"hrlxzfp4noajRWPVp_UC\\";
13633+
export var ghi_TEST_3 = \\"hrlxzfp4noajRWPVp_UC\\";
13634+
export var my_class_TEST_1 = \\"Wr3wRBz8YFj3jWOISgia\\";
13635+
export var my_class_TEST_3 = \\"Wr3wRBz8YFj3jWOISgia\\";
13636+
export var other_TEST_1 = \\"N3CMKfMxK4wVelMiMLmQ\\";
13637+
export var other_TEST_3 = \\"N3CMKfMxK4wVelMiMLmQ\\";
13638+
export var other_other_TEST_1 = \\"pgrezI_6B1TIw29jJdO0\\";
13639+
export var other_other_TEST_3 = \\"pgrezI_6B1TIw29jJdO0\\";
13640+
export var green_TEST_1 = \\"_wneqlVHVVNWG_hbM3Ti\\";
13641+
export var green_TEST_3 = \\"_wneqlVHVVNWG_hbM3Ti\\";
13642+
export default ___CSS_LOADER_EXPORT___;
13643+
"
13644+
`;
13645+
13646+
exports[`"modules" option should work with composes when the "exportLocalsConvention" is function and return array names: result 1`] = `
13647+
Array [
13648+
Array [
13649+
"../../src/index.js??ruleSet[1].rules[0].use[0]!./modules/namedExport/composes/values.css",
13650+
"
13651+
",
13652+
"",
13653+
],
13654+
Array [
13655+
"./modules/namedExport/composes/composes.css",
13656+
".hrlxzfp4noajRWPVp_UC {
13657+
color: red;
13658+
}
13659+
13660+
.Wr3wRBz8YFj3jWOISgia {
13661+
color: white;
13662+
}
13663+
13664+
.N3CMKfMxK4wVelMiMLmQ {
13665+
display: (min-width: 320px);
13666+
}
13667+
13668+
.pgrezI_6B1TIw29jJdO0 {
13669+
width: red;
13670+
}
13671+
13672+
._wneqlVHVVNWG_hbM3Ti {
13673+
color: green;
13674+
}
13675+
",
13676+
"",
13677+
],
13678+
]
13679+
`;
13680+
13681+
exports[`"modules" option should work with composes when the "exportLocalsConvention" is function and return array names: warnings 1`] = `Array []`;
13682+
1355513683
exports[`"modules" option should work with composes when the "exportLocalsConvention" is function: errors 1`] = `Array []`;
1355613684

1355713685
exports[`"modules" option should work with composes when the "exportLocalsConvention" is function: module 1`] = `

test/modules-option.test.js

+49
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,32 @@ describe('"modules" option', () => {
12111211
expect(getErrors(stats)).toMatchSnapshot("errors");
12121212
});
12131213

1214+
it('should work and respect the "exportLocalsConvention" option with the "function" type and returns array names', async () => {
1215+
const compiler = getCompiler(
1216+
"./modules/localsConvention/localsConvention.js",
1217+
{
1218+
modules: {
1219+
mode: "local",
1220+
exportLocalsConvention: (localName) => [
1221+
`${localName.replace(/-/g, "_")}_TEST_1`,
1222+
`${localName.replace(/-/g, "_")}_TEST_1`,
1223+
`${localName.replace(/-/g, "_")}_TEST_3`,
1224+
],
1225+
},
1226+
}
1227+
);
1228+
const stats = await compile(compiler);
1229+
1230+
expect(
1231+
getModuleSource("./modules/localsConvention/localsConvention.css", stats)
1232+
).toMatchSnapshot("module");
1233+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
1234+
"result"
1235+
);
1236+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
1237+
expect(getErrors(stats)).toMatchSnapshot("errors");
1238+
});
1239+
12141240
it('should work and respect the "localConvention" option with the "camelCase" value', async () => {
12151241
const compiler = getCompiler(
12161242
"./modules/localsConvention/localsConvention.js",
@@ -1537,6 +1563,29 @@ describe('"modules" option', () => {
15371563
expect(getErrors(stats)).toMatchSnapshot("errors");
15381564
});
15391565

1566+
it('should work with composes when the "exportLocalsConvention" is function and return array names', async () => {
1567+
const compiler = getCompiler("./modules/namedExport/composes/composes.js", {
1568+
modules: {
1569+
namedExport: true,
1570+
exportLocalsConvention: (localName) => [
1571+
`${localName.replace(/-/g, "_")}_TEST_1`,
1572+
`${localName.replace(/-/g, "_")}_TEST_1`,
1573+
`${localName.replace(/-/g, "_")}_TEST_3`,
1574+
],
1575+
},
1576+
});
1577+
const stats = await compile(compiler);
1578+
1579+
expect(
1580+
getModuleSource("./modules/namedExport/composes/composes.css", stats)
1581+
).toMatchSnapshot("module");
1582+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
1583+
"result"
1584+
);
1585+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
1586+
expect(getErrors(stats)).toMatchSnapshot("errors");
1587+
});
1588+
15401589
it('should work with composes when the "exportLocalsConvention" is function', async () => {
15411590
const compiler = getCompiler("./modules/namedExport/composes/composes.js", {
15421591
modules: {

0 commit comments

Comments
 (0)