Skip to content

Commit de118a2

Browse files
committed
feat(localsconvention): added function
1 parent b95a779 commit de118a2

7 files changed

+120
-32
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ This may change in the future when the module system (i. e. webpack) supports lo
783783

784784
### `localsConvention`
785785

786-
Type: `String`
786+
Type: `String` | `Function`
787787
Default: `'asIs'`
788788

789789
Style of exported classnames.
@@ -798,6 +798,8 @@ By default, the exported JSON keys mirror the class names (i.e `asIs` value).
798798
| **`'dashes'`** | `{String}` | Only dashes in class names will be camelized |
799799
| **`'dashesOnly'`** | `{String}` | Dashes in class names will be camelized, the original class name will be removed from the locals |
800800

801+
If you want more control over exported classnames, you can also provide a function.
802+
801803
**file.css**
802804

803805
```css
@@ -821,7 +823,7 @@ module.exports = {
821823
test: /\.css$/i,
822824
loader: 'css-loader',
823825
options: {
824-
localsConvention: 'camelCase',
826+
localsConvention: (classname) => classname.toLowerCase(),
825827
},
826828
},
827829
],

src/options.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,14 @@
8989
},
9090
"localsConvention": {
9191
"description": "Style of exported classnames (https://github.com/webpack-contrib/css-loader#localsconvention).",
92-
"enum": ["asIs", "camelCase", "camelCaseOnly", "dashes", "dashesOnly"]
92+
"anyOf": [
93+
{
94+
"enum": ["asIs", "camelCase", "camelCaseOnly", "dashes", "dashesOnly"]
95+
},
96+
{
97+
"instanceof": "Function"
98+
}
99+
]
93100
},
94101
"onlyLocals": {
95102
"description": "Export only locals (https://github.com/webpack-contrib/css-loader#onlylocals).",

src/utils.js

+34-26
Original file line numberDiff line numberDiff line change
@@ -425,39 +425,47 @@ function getExportCode(
425425
exports.forEach((item) => {
426426
const { name, value } = item;
427427

428-
switch (localsConvention) {
429-
case 'camelCase': {
430-
addExportedLocal(name, value);
428+
if (typeof localsConvention === 'function') {
429+
const modifiedName = localsConvention(name);
431430

432-
const modifiedName = camelCase(name);
431+
if (modifiedName !== name) {
432+
addExportedLocal(modifiedName, value);
433+
}
434+
} else {
435+
switch (localsConvention) {
436+
case 'camelCase': {
437+
addExportedLocal(name, value);
438+
439+
const modifiedName = camelCase(name);
433440

434-
if (modifiedName !== name) {
435-
addExportedLocal(modifiedName, value);
441+
if (modifiedName !== name) {
442+
addExportedLocal(modifiedName, value);
443+
}
444+
break;
436445
}
437-
break;
438-
}
439-
case 'camelCaseOnly': {
440-
addExportedLocal(camelCase(name), value);
441-
break;
442-
}
443-
case 'dashes': {
444-
addExportedLocal(name, value);
446+
case 'camelCaseOnly': {
447+
addExportedLocal(camelCase(name), value);
448+
break;
449+
}
450+
case 'dashes': {
451+
addExportedLocal(name, value);
445452

446-
const modifiedName = dashesCamelCase(name);
453+
const modifiedName = dashesCamelCase(name);
447454

448-
if (modifiedName !== name) {
449-
addExportedLocal(modifiedName, value);
455+
if (modifiedName !== name) {
456+
addExportedLocal(modifiedName, value);
457+
}
458+
break;
450459
}
451-
break;
452-
}
453-
case 'dashesOnly': {
454-
addExportedLocal(dashesCamelCase(name), value);
455-
break;
460+
case 'dashesOnly': {
461+
addExportedLocal(dashesCamelCase(name), value);
462+
break;
463+
}
464+
case 'asIs':
465+
default:
466+
addExportedLocal(name, value);
467+
break;
456468
}
457-
case 'asIs':
458-
default:
459-
addExportedLocal(name, value);
460-
break;
461469
}
462470
});
463471

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

+47
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,50 @@ a {
287287
`;
288288

289289
exports[`"localsConvention" option should work with a value equal to "dashesOnly": warnings 1`] = `Array []`;
290+
291+
exports[`"localsConvention" option should work with a value equal to a custom function: errors 1`] = `Array []`;
292+
293+
exports[`"localsConvention" option should work with a value equal to a custom function: module 1`] = `
294+
"// Imports
295+
var ___CSS_LOADER_API_IMPORT___ = require(\\"../../../../src/runtime/api.js\\");
296+
exports = ___CSS_LOADER_API_IMPORT___(false);
297+
// Module
298+
exports.push([module.id, \\".erBXHZCN_thRYfCnk-aH8 {\\\\n color: blue;\\\\n}\\\\n\\\\n._2YsQE-S0o0NRXfC6XNApz2 {\\\\n color: blue;\\\\n}\\\\n\\\\n._3gGBcJHZU3seQVP5aq7Ksq {\\\\n color: red;\\\\n}\\\\n\\\\na {\\\\n color: yellow;\\\\n}\\\\n\\", \\"\\"]);
299+
// Exports
300+
exports.locals = {
301+
\\"FOO\\": \\"bar\\",
302+
\\"MY-BTN-INFO_IS-DISABLED\\": \\"value\\",
303+
\\"BTN-INFO_IS-DISABLED\\": \\"erBXHZCN_thRYfCnk-aH8\\",
304+
\\"BTN--INFO_IS-DISABLED_1\\": \\"_2YsQE-S0o0NRXfC6XNApz2\\",
305+
\\"SIMPLE\\": \\"_3gGBcJHZU3seQVP5aq7Ksq\\"
306+
};
307+
module.exports = exports;
308+
"
309+
`;
310+
311+
exports[`"localsConvention" option should work with a value equal to a custom function: result 1`] = `
312+
Array [
313+
Array [
314+
"./modules/localsConvention/localsConvention.css",
315+
".erBXHZCN_thRYfCnk-aH8 {
316+
color: blue;
317+
}
318+
319+
._2YsQE-S0o0NRXfC6XNApz2 {
320+
color: blue;
321+
}
322+
323+
._3gGBcJHZU3seQVP5aq7Ksq {
324+
color: red;
325+
}
326+
327+
a {
328+
color: yellow;
329+
}
330+
",
331+
"",
332+
],
333+
]
334+
`;
335+
336+
exports[`"localsConvention" option should work with a value equal to a custom function: warnings 1`] = `Array []`;

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ exports[`validate options should throw an error on the "importLoaders" option wi
2929
exports[`validate options should throw an error on the "localsConvention" option with "unknown" value 1`] = `
3030
"Invalid options object. CSS Loader has been initialised using an options object that does not match the API schema.
3131
- options.localsConvention should be one of these:
32-
\\"asIs\\" | \\"camelCase\\" | \\"camelCaseOnly\\" | \\"dashes\\" | \\"dashesOnly\\"
33-
-> Style of exported classnames (https://github.com/webpack-contrib/css-loader#localsconvention)."
32+
\\"asIs\\" | \\"camelCase\\" | \\"camelCaseOnly\\" | \\"dashes\\" | \\"dashesOnly\\" | function
33+
-> Style of exported classnames (https://github.com/webpack-contrib/css-loader#localsconvention).
34+
Details:
35+
* options.localsConvention should be one of these:
36+
\\"asIs\\" | \\"camelCase\\" | \\"camelCaseOnly\\" | \\"dashes\\" | \\"dashesOnly\\"
37+
* options.localsConvention should be an instance of function."
3438
`;
3539

3640
exports[`validate options should throw an error on the "modules" option with "{"context":true}" value 1`] = `

test/localsConvention-option.test.js

+20
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,24 @@ describe('"localsConvention" option', () => {
126126
expect(getWarnings(stats)).toMatchSnapshot('warnings');
127127
expect(getErrors(stats)).toMatchSnapshot('errors');
128128
});
129+
130+
it('should work with a value equal to a custom function', async () => {
131+
const compiler = getCompiler(
132+
'./modules/localsConvention/localsConvention.js',
133+
{
134+
modules: true,
135+
localsConvention: (className) => className.toUpperCase(),
136+
}
137+
);
138+
const stats = await compile(compiler);
139+
140+
expect(
141+
getModuleSource('./modules/localsConvention/localsConvention.css', stats)
142+
).toMatchSnapshot('module');
143+
expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot(
144+
'result'
145+
);
146+
expect(getWarnings(stats)).toMatchSnapshot('warnings');
147+
expect(getErrors(stats)).toMatchSnapshot('errors');
148+
});
129149
});

test/validate-options.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('validate options', () => {
4848
failure: ['true'],
4949
},
5050
localsConvention: {
51-
success: ['camelCase', 'camelCaseOnly', 'dashes', 'dashesOnly'],
51+
success: ['camelCase', 'camelCaseOnly', 'dashes', 'dashesOnly', () => {}],
5252
failure: ['unknown'],
5353
},
5454
importLoaders: {

0 commit comments

Comments
 (0)