Skip to content

Commit

Permalink
feat: namedexport option could be a function
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Jul 19, 2021
1 parent cc0c1b4 commit 3928488
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 8 deletions.
12 changes: 11 additions & 1 deletion src/index.js
Expand Up @@ -216,7 +216,17 @@ export default async function loader(content, map, meta) {
}

const importCode = getImportCode(imports, options);
const moduleCode = getModuleCode(result, api, replacements, options, this);

let moduleCode;

try {
moduleCode = getModuleCode(result, api, replacements, options, this);
} catch (error) {
callback(error);

return;
}

const exportCode = getExportCode(
exports,
replacements,
Expand Down
9 changes: 8 additions & 1 deletion src/options.json
Expand Up @@ -135,7 +135,14 @@
"namedExport": {
"description": "Enables/disables ES modules named export for locals.",
"link": "https://github.com/webpack-contrib/css-loader#namedexport",
"type": "boolean"
"anyOf": [
{
"type": "boolean"
},
{
"instanceof": "Function"
}
]
},
"exportGlobals": {
"description": "Allows to export names from global class or id, so you can use that as local name.",
Expand Down
26 changes: 23 additions & 3 deletions src/utils.js
Expand Up @@ -484,7 +484,11 @@ function getFilter(filter, resourcePath) {
};
}

function getValidLocalName(localName, exportLocalsConvention) {
function getValidLocalName(localName, exportLocalsConvention, namedExportFn) {
if (namedExportFn) {
return namedExportFn(localName);
}

if (exportLocalsConvention === "dashesOnly") {
return dashesCamelCase(localName);
}
Expand Down Expand Up @@ -546,6 +550,10 @@ function getModulesOptions(rawOptions, loaderContext) {
...rawModulesOptions,
};

if (typeof modulesOptions.namedExport === "function") {
modulesOptions.namedExportFn = modulesOptions.namedExport;
}

if (typeof modulesOptions.auto === "boolean") {
const isModules = modulesOptions.auto && IS_MODULES.test(resourcePath);

Expand Down Expand Up @@ -914,7 +922,8 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
? `" + ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
options.modules.exportLocalsConvention,
options.modules.namedExportFn
)
)}] + "`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
Expand Down Expand Up @@ -970,6 +979,13 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
};

for (const { name, value } of exports) {
if (options.modules.namedExportFn) {
addExportToLocalsCode(options.modules.namedExportFn(name), value);

// eslint-disable-next-line no-continue
continue;
}

switch (options.modules.exportLocalsConvention) {
case "camelCase": {
addExportToLocalsCode(name, value);
Expand Down Expand Up @@ -1015,7 +1031,11 @@ function getExportCode(exports, replacements, needToUseIcssPlugin, options) {
localsCode = localsCode.replace(new RegExp(replacementName, "g"), () => {
if (options.modules.namedExport) {
return `" + ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(localName, options.modules.exportLocalsConvention)
getValidLocalName(
localName,
options.modules.exportLocalsConvention,
options.modules.namedExportFn
)
)}] + "`;
} else if (options.modules.exportOnlyLocals) {
return `" + ${importName}[${JSON.stringify(localName)}] + "`;
Expand Down
149 changes: 149 additions & 0 deletions test/__snapshots__/modules-option.test.js.snap
Expand Up @@ -1749,6 +1749,15 @@ Error: The \\"modules.namedExport\\" option requires the \\"esModules\\" option

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

exports[`"modules" option should throw error when the "namedExport" function throw error: errors 1`] = `
Array [
"ModuleBuildError: Module build failed (from \`replaced original path\`):
Error: namedExportFn error",
]
`;

exports[`"modules" option should throw error when the "namedExport" function throw error: warnings 1`] = `Array []`;

exports[`"modules" option should throw error with composes when the "namedExport" is enabled and "exportLocalsConvention" options has invalid value: errors 1`] = `
Array [
"ModuleBuildError: Module build failed (from \`replaced original path\`):
Expand Down Expand Up @@ -4606,6 +4615,52 @@ h1 #pWzFEVR2SnlD5kUmOw_N {

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

exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: errors 1`] = `Array []`;

exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
// Module
___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\\", \\"\\"]);
// Exports
export var header_baz_TEST = \\"header-baz\\";
export var body_TEST = \\"body\\";
export var footer_TEST = \\"footer\\";
export default ___CSS_LOADER_EXPORT___;
"
`;

exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: result 1`] = `
Object {
"css": Array [
Array [
"./modules/namedExport/template-2/index.css",
".header-baz {
color: red;
}

.body {
color: coral;
}

.footer {
color: blue;
}
",
"",
],
],
"html": "
<div class=\\"header-baz\\">
<div class=\\"body\\">
<div class=\\"footer\\">
",
}
`;

exports[`"modules" option should work js template with "namedExport" option when "namedExport" option is function: warnings 1`] = `Array []`;

exports[`"modules" option should work js template with "namedExport" option: errors 1`] = `Array []`;

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

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

exports[`"modules" option should work when the "namedExport" option is function: errors 1`] = `Array []`;

exports[`"modules" option should work when the "namedExport" option is function: module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
// Module
___CSS_LOADER_EXPORT___.push([module.id, \\"._pV82SQbfroU2_cQrb3p {\\\\n color: red;\\\\n}\\\\n\\\\n.bar {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]);
// Exports
export var bar_baz_TEST = \\"_pV82SQbfroU2_cQrb3p\\";
export default ___CSS_LOADER_EXPORT___;
"
`;

exports[`"modules" option should work when the "namedExport" option is function: result 1`] = `
Array [
Array [
"./modules/namedExport/base/index.css",
"._pV82SQbfroU2_cQrb3p {
color: red;
}

.bar {
color: red;
}
",
"",
],
]
`;

exports[`"modules" option should work when the "namedExport" option is function: warnings 1`] = `Array []`;

exports[`"modules" option should work with "exportOnlyLocals" and "esModule" with "false" value options: errors 1`] = `Array []`;

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

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

exports[`"modules" option should work with composes when the "namedExport" is function: errors 1`] = `Array []`;

exports[`"modules" option should work with composes when the "namedExport" is function: module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
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\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
// Module
___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\\", \\"\\"]);
// Exports
export var v_def_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_def_TEST\\"] + \\"\\";
export var v_other_other_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"v_other_other_TEST\\"] + \\"\\";
export var s_white_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"s_white_TEST\\"] + \\"\\";
export var m_small_TEST = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"m_small_TEST\\"] + \\"\\";
export var ghi_TEST = \\"_ghi\\";
export var my_class_TEST = \\"_my-class\\";
export var other_TEST = \\"_other\\";
export var other_other_TEST = \\"_other-other\\";
export var green_TEST = \\"_green\\";
export default ___CSS_LOADER_EXPORT___;
"
`;

exports[`"modules" option should work with composes when the "namedExport" is function: result 1`] = `
Array [
Array [
"../../src/index.js??ruleSet[1].rules[0].use[0]!./modules/namedExport/composes/values.css",
"
",
"",
],
Array [
"./modules/namedExport/composes/composes.css",
"._ghi {
color: red;
}

._my-class {
color: white;
}

._other {
display: (min-width: 320px);
}

._other-other {
width: red;
}

._green {
color: green;
}
",
"",
],
]
`;

exports[`"modules" option should work with composes when the "namedExport" is function: warnings 1`] = `Array []`;

exports[`"modules" option should work with the "[local]" placeholder for the "localIdentName" option: errors 1`] = `Array []`;

exports[`"modules" option should work with the "[local]" placeholder for the "localIdentName" option: module 1`] = `
Expand Down
15 changes: 12 additions & 3 deletions test/__snapshots__/validate-options.test.js.snap
Expand Up @@ -219,9 +219,18 @@ exports[`validate options should throw an error on the "modules" option with "{"
exports[`validate options should throw an error on the "modules" option with "{"namedExport":"invalid"}" value 1`] = `
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options.modules.namedExport should be a boolean.
-> Enables/disables ES modules named export for locals.
-> Read more at https://github.com/webpack-contrib/css-loader#namedexport"
- options.modules should be one of these:
boolean | \\"local\\" | \\"global\\" | \\"pure\\" | \\"icss\\" | object { auto?, mode?, localIdentName?, localIdentContext?, localIdentHashSalt?, localIdentHashFunction?, localIdentHashDigest?, localIdentHashDigestLength?, localIdentRegExp?, getLocalIdent?, namedExport?, exportGlobals?, exportLocalsConvention?, exportOnlyLocals? }
-> Allows to enable/disable CSS Modules or ICSS and setup configuration.
-> Read more at https://github.com/webpack-contrib/css-loader#modules
Details:
* options.modules.namedExport should be one of these:
boolean | function
-> Enables/disables ES modules named export for locals.
-> Read more at https://github.com/webpack-contrib/css-loader#namedexport
Details:
* options.modules.namedExport should be a boolean.
* options.modules.namedExport should be an instance of function."
`;
exports[`validate options should throw an error on the "modules" option with "globals" value 1`] = `
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/modules/namedExport/template-2/index.css
@@ -0,0 +1,11 @@
:local(.header-baz) {
color: red;
}

:local(.body) {
color: coral;
}

:local(.footer) {
color: blue;
}
8 changes: 8 additions & 0 deletions test/fixtures/modules/namedExport/template-2/index.js
@@ -0,0 +1,8 @@
import css from './index.css';
import html from './template.js';

const result = {css, html};

__export__ = result;

export default result;
11 changes: 11 additions & 0 deletions test/fixtures/modules/namedExport/template-2/template.js
@@ -0,0 +1,11 @@
import { header_baz_TEST, body_TEST, footer_TEST } from './index.css';

let html = '\n';

html += `<div class="${header_baz_TEST}">\n`;
html += `<div class="${body_TEST}">\n`;
html += `<div class="${footer_TEST}">\n`;

__export__ = html;

export default html;

0 comments on commit 3928488

Please sign in to comment.