Skip to content

Commit 3928488

Browse files
feat: namedexport option could be a function
1 parent cc0c1b4 commit 3928488

File tree

10 files changed

+305
-8
lines changed

10 files changed

+305
-8
lines changed

src/index.js

Lines changed: 11 additions & 1 deletion
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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,14 @@
135135
"namedExport": {
136136
"description": "Enables/disables ES modules named export for locals.",
137137
"link": "https://github.com/webpack-contrib/css-loader#namedexport",
138-
"type": "boolean"
138+
"anyOf": [
139+
{
140+
"type": "boolean"
141+
},
142+
{
143+
"instanceof": "Function"
144+
}
145+
]
139146
},
140147
"exportGlobals": {
141148
"description": "Allows to export names from global class or id, so you can use that as local name.",

src/utils.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,11 @@ function getFilter(filter, resourcePath) {
484484
};
485485
}
486486

487-
function getValidLocalName(localName, exportLocalsConvention) {
487+
function getValidLocalName(localName, exportLocalsConvention, namedExportFn) {
488+
if (namedExportFn) {
489+
return namedExportFn(localName);
490+
}
491+
488492
if (exportLocalsConvention === "dashesOnly") {
489493
return dashesCamelCase(localName);
490494
}
@@ -546,6 +550,10 @@ function getModulesOptions(rawOptions, loaderContext) {
546550
...rawModulesOptions,
547551
};
548552

553+
if (typeof modulesOptions.namedExport === "function") {
554+
modulesOptions.namedExportFn = modulesOptions.namedExport;
555+
}
556+
549557
if (typeof modulesOptions.auto === "boolean") {
550558
const isModules = modulesOptions.auto && IS_MODULES.test(resourcePath);
551559

@@ -914,7 +922,8 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
914922
? `" + ${importName}_NAMED___[${JSON.stringify(
915923
getValidLocalName(
916924
localName,
917-
options.modules.exportLocalsConvention
925+
options.modules.exportLocalsConvention,
926+
options.modules.namedExportFn
918927
)
919928
)}] + "`
920929
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
@@ -970,6 +979,13 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
970979
};
971980

972981
for (const { name, value } of exports) {
982+
if (options.modules.namedExportFn) {
983+
addExportToLocalsCode(options.modules.namedExportFn(name), value);
984+
985+
// eslint-disable-next-line no-continue
986+
continue;
987+
}
988+
973989
switch (options.modules.exportLocalsConvention) {
974990
case "camelCase": {
975991
addExportToLocalsCode(name, value);
@@ -1015,7 +1031,11 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
10151031
localsCode = localsCode.replace(new RegExp(replacementName, "g"), () => {
10161032
if (options.modules.namedExport) {
10171033
return `" + ${importName}_NAMED___[${JSON.stringify(
1018-
getValidLocalName(localName, options.modules.exportLocalsConvention)
1034+
getValidLocalName(
1035+
localName,
1036+
options.modules.exportLocalsConvention,
1037+
options.modules.namedExportFn
1038+
)
10191039
)}] + "`;
10201040
} else if (options.modules.exportOnlyLocals) {
10211041
return `" + ${importName}[${JSON.stringify(localName)}] + "`;

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

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,15 @@ Error: The \\"modules.namedExport\\" option requires the \\"esModules\\" option
17491749

17501750
exports[`"modules" option should throw an error when the "namedExport" option is "true", but the "esModule" is "false": warnings 1`] = `Array []`;
17511751

1752+
exports[`"modules" option should throw error when the "namedExport" function throw error: errors 1`] = `
1753+
Array [
1754+
"ModuleBuildError: Module build failed (from \`replaced original path\`):
1755+
Error: namedExportFn error",
1756+
]
1757+
`;
1758+
1759+
exports[`"modules" option should throw error when the "namedExport" function throw error: warnings 1`] = `Array []`;
1760+
17521761
exports[`"modules" option should throw error with composes when the "namedExport" is enabled and "exportLocalsConvention" options has invalid value: errors 1`] = `
17531762
Array [
17541763
"ModuleBuildError: Module build failed (from \`replaced original path\`):
@@ -4606,6 +4615,52 @@ h1 #pWzFEVR2SnlD5kUmOw_N {
46064615

46074616
exports[`"modules" option should work and support "pure" mode: warnings 1`] = `Array []`;
46084617

4618+
exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: errors 1`] = `Array []`;
4619+
4620+
exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: module 1`] = `
4621+
"// Imports
4622+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
4623+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
4624+
// Module
4625+
___CSS_LOADER_EXPORT___.push([module.id, \\".header-baz {\\\\n color: red;\\\\n}\\\\n\\\\n.body {\\\\n color: coral;\\\\n}\\\\n\\\\n.footer {\\\\n color: blue;\\\\n}\\\\n\\", \\"\\"]);
4626+
// Exports
4627+
export var header_baz_TEST = \\"header-baz\\";
4628+
export var body_TEST = \\"body\\";
4629+
export var footer_TEST = \\"footer\\";
4630+
export default ___CSS_LOADER_EXPORT___;
4631+
"
4632+
`;
4633+
4634+
exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: result 1`] = `
4635+
Object {
4636+
"css": Array [
4637+
Array [
4638+
"./modules/namedExport/template-2/index.css",
4639+
".header-baz {
4640+
color: red;
4641+
}
4642+
4643+
.body {
4644+
color: coral;
4645+
}
4646+
4647+
.footer {
4648+
color: blue;
4649+
}
4650+
",
4651+
"",
4652+
],
4653+
],
4654+
"html": "
4655+
<div class=\\"header-baz\\">
4656+
<div class=\\"body\\">
4657+
<div class=\\"footer\\">
4658+
",
4659+
}
4660+
`;
4661+
4662+
exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: warnings 1`] = `Array []`;
4663+
46094664
exports[`"modules" option should work js template with "namedExport" option: errors 1`] = `Array []`;
46104665

46114666
exports[`"modules" option should work js template with "namedExport" option: module 1`] = `
@@ -4805,6 +4860,39 @@ Array [
48054860

48064861
exports[`"modules" option should work when the "namedExport" is enabled and the "exportLocalsConvention" options has "dashesOnly" value: warnings 1`] = `Array []`;
48074862

4863+
exports[`"modules" option should work when the "namedExport" option is function: errors 1`] = `Array []`;
4864+
4865+
exports[`"modules" option should work when the "namedExport" option is function: module 1`] = `
4866+
"// Imports
4867+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
4868+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
4869+
// Module
4870+
___CSS_LOADER_EXPORT___.push([module.id, \\"._pV82SQbfroU2_cQrb3p {\\\\n color: red;\\\\n}\\\\n\\\\n.bar {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]);
4871+
// Exports
4872+
export var bar_baz_TEST = \\"_pV82SQbfroU2_cQrb3p\\";
4873+
export default ___CSS_LOADER_EXPORT___;
4874+
"
4875+
`;
4876+
4877+
exports[`"modules" option should work when the "namedExport" option is function: result 1`] = `
4878+
Array [
4879+
Array [
4880+
"./modules/namedExport/base/index.css",
4881+
"._pV82SQbfroU2_cQrb3p {
4882+
color: red;
4883+
}
4884+
4885+
.bar {
4886+
color: red;
4887+
}
4888+
",
4889+
"",
4890+
],
4891+
]
4892+
`;
4893+
4894+
exports[`"modules" option should work when the "namedExport" option is function: warnings 1`] = `Array []`;
4895+
48084896
exports[`"modules" option should work with "exportOnlyLocals" and "esModule" with "false" value options: errors 1`] = `Array []`;
48094897

48104898
exports[`"modules" option should work with "exportOnlyLocals" and "esModule" with "false" value options: module 1`] = `
@@ -13473,6 +13561,67 @@ Array [
1347313561

1347413562
exports[`"modules" option should work with composes when the "namedExport" is enabled and "exportLocalsConvention" options has "dashesOnly" value: warnings 1`] = `Array []`;
1347513563

13564+
exports[`"modules" option should work with composes when the "namedExport" is function: errors 1`] = `Array []`;
13565+
13566+
exports[`"modules" option should work with composes when the "namedExport" is function: module 1`] = `
13567+
"// Imports
13568+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
13569+
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\\";
13570+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
13571+
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
13572+
// Module
13573+
___CSS_LOADER_EXPORT___.push([module.id, \\"._ghi {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST\\"] + \\";\\\\n}\\\\n\\\\n._my-class {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"s_white_TEST\\"] + \\";\\\\n}\\\\n\\\\n._other {\\\\n display: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"m_small_TEST\\"] + \\";\\\\n}\\\\n\\\\n._other-other {\\\\n width: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST\\"] + \\";\\\\n}\\\\n\\\\n._green {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_other_other_TEST\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
13574+
// Exports
13575+
export var v_def_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST\\"] + \\"\\";
13576+
export var v_other_other_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_other_other_TEST\\"] + \\"\\";
13577+
export var s_white_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"s_white_TEST\\"] + \\"\\";
13578+
export var m_small_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"m_small_TEST\\"] + \\"\\";
13579+
export var ghi_TEST = \\"_ghi\\";
13580+
export var my_class_TEST = \\"_my-class\\";
13581+
export var other_TEST = \\"_other\\";
13582+
export var other_other_TEST = \\"_other-other\\";
13583+
export var green_TEST = \\"_green\\";
13584+
export default ___CSS_LOADER_EXPORT___;
13585+
"
13586+
`;
13587+
13588+
exports[`"modules" option should work with composes when the "namedExport" is function: result 1`] = `
13589+
Array [
13590+
Array [
13591+
"../../src/index.js??ruleSet[1].rules[0].use[0]!./modules/namedExport/composes/values.css",
13592+
"
13593+
",
13594+
"",
13595+
],
13596+
Array [
13597+
"./modules/namedExport/composes/composes.css",
13598+
"._ghi {
13599+
color: red;
13600+
}
13601+
13602+
._my-class {
13603+
color: white;
13604+
}
13605+
13606+
._other {
13607+
display: (min-width: 320px);
13608+
}
13609+
13610+
._other-other {
13611+
width: red;
13612+
}
13613+
13614+
._green {
13615+
color: green;
13616+
}
13617+
",
13618+
"",
13619+
],
13620+
]
13621+
`;
13622+
13623+
exports[`"modules" option should work with composes when the "namedExport" is function: warnings 1`] = `Array []`;
13624+
1347613625
exports[`"modules" option should work with the "[local]" placeholder for the "localIdentName" option: errors 1`] = `Array []`;
1347713626

1347813627
exports[`"modules" option should work with the "[local]" placeholder for the "localIdentName" option: module 1`] = `

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,18 @@ exports[`validate options should throw an error on the "modules" option with "{"
219219
220220
exports[`validate options should throw an error on the "modules" option with "{"namedExport":"invalid"}" value 1`] = `
221221
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
222-
- options.modules.namedExport should be a boolean.
223-
-> Enables/disables ES modules named export for locals.
224-
-> Read more at https://github.com/webpack-contrib/css-loader#namedexport"
222+
- options.modules should be one of these:
223+
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | \\"icss\\" | object { auto?, mode?, localIdentName?, localIdentContext?, localIdentHashSalt?, localIdentHashFunction?, localIdentHashDigest?, localIdentHashDigestLength?, localIdentRegExp?, getLocalIdent?, namedExport?, exportGlobals?, exportLocalsConvention?, exportOnlyLocals? }
224+
-> Allows to enable/disable CSS Modules or ICSS and setup configuration.
225+
-> Read more at https://github.com/webpack-contrib/css-loader#modules
226+
Details:
227+
* options.modules.namedExport should be one of these:
228+
boolean | function
229+
-> Enables/disables ES modules named export for locals.
230+
-> Read more at https://github.com/webpack-contrib/css-loader#namedexport
231+
Details:
232+
* options.modules.namedExport should be a boolean.
233+
* options.modules.namedExport should be an instance of function."
225234
`;
226235
227236
exports[`validate options should throw an error on the "modules" option with "globals" value 1`] = `
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
:local(.header-baz) {
2+
color: red;
3+
}
4+
5+
:local(.body) {
6+
color: coral;
7+
}
8+
9+
:local(.footer) {
10+
color: blue;
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import css from './index.css';
2+
import html from './template.js';
3+
4+
const result = {css, html};
5+
6+
__export__ = result;
7+
8+
export default result;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { header_baz_TEST, body_TEST, footer_TEST } from './index.css';
2+
3+
let html = '\n';
4+
5+
html += `<div class="${header_baz_TEST}">\n`;
6+
html += `<div class="${body_TEST}">\n`;
7+
html += `<div class="${footer_TEST}">\n`;
8+
9+
__export__ = html;
10+
11+
export default html;

0 commit comments

Comments
 (0)