From 40e1668b8366f0df63343efe706ba848c2b5dfb2 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 4 Apr 2024 15:33:33 +0300 Subject: [PATCH] feat: added dashed variants for the `exportLocalsConvention` options --- README.md | 42 +++++++++---------- src/options.json | 6 ++- src/utils.js | 19 ++++++--- .../__snapshots__/modules-option.test.js.snap | 42 +++++++++---------- .../validate-options.test.js.snap | 4 +- test/modules-option.test.js | 5 +-- test/validate-options.test.js | 5 ++- 7 files changed, 68 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 21aec0a9..a62a9c01 100644 --- a/README.md +++ b/README.md @@ -320,11 +320,11 @@ type modules = namedExport: boolean; exportGlobals: boolean; exportLocalsConvention: - | "asIs" - | "camelCase" - | "camelCaseOnly" + | "as-is" + | "camel-case" + | "camel-case-only" | "dashes" - | "dashesOnly" + | "dashes-only" | ((name: string) => string); exportOnlyLocals: boolean; }; @@ -602,7 +602,7 @@ module.exports = { localIdentContext: path.resolve(__dirname, "src"), localIdentHashSalt: "my-custom-hash", namedExport: true, - exportLocalsConvention: "camelCase", + exportLocalsConvention: "as-is", exportOnlyLocals: false, }, }, @@ -1152,7 +1152,7 @@ Enables/disables ES modules named export for locals. > **Warning** > > It is not allowed to use JavaScript reserved words in css class names unless -> `exportLocalsConvention` is `"asIs"`. +> `exportLocalsConvention` is `"as-is"`. **styles.css** @@ -1171,7 +1171,7 @@ Enables/disables ES modules named export for locals. import * as styles from "./styles.css"; console.log(styles.fooBaz, styles.bar); -// or if using `exportLocalsConvention: "asIs"`: +// or if using `exportLocalsConvention: "as-is"`: console.log(styles["foo-baz"], styles.bar); ``` @@ -1239,29 +1239,29 @@ Type: ```ts type exportLocalsConvention = - | "asIs" - | "camelCase" - | "camelCaseOnly" + | "as-is" + | "camel-case" + | "camel-case-only" | "dashes" - | "dashesOnly" + | "dashes-only" | ((name: string) => string); ``` -Default: based on the `modules.namedExport` option value, if `true` - `camelCaseOnly`, otherwise `asIs` +Default: based on the `modules.namedExport` option value, if `true` - `camelCaseOnly`, otherwise `as-is` Style of exported class names. ###### `string` -By default, the exported JSON keys mirror the class names (i.e `asIs` value). +By default, the exported JSON keys mirror the class names (i.e `as-is` value). -| Name | Type | Description | -| :-------------------: | :------: | :----------------------------------------------------------------------------------------------- | -| **`'asIs'`** | `string` | Class names will be exported as is. | -| **`'camelCase'`** | `string` | Class names will be camelized, the original class name will not to be removed from the locals | -| **`'camelCaseOnly'`** | `string` | Class names will be camelized, the original class name will be removed from the locals | -| **`'dashes'`** | `string` | Only dashes in class names will be camelized | -| **`'dashesOnly'`** | `string` | Dashes in class names will be camelized, the original class name will be removed from the locals | +| Name | Type | Description | +| :---------------------: | :------: | :----------------------------------------------------------------------------------------------- | +| **`'as-is'`** | `string` | Class names will be exported as is. | +| **`'camel-case'`** | `string` | Class names will be camelized, the original class name will not to be removed from the locals | +| **`'camel-case-only'`** | `string` | Class names will be camelized, the original class name will be removed from the locals | +| **`'dashes'`** | `string` | Only dashes in class names will be camelized | +| **`'dashes-only'`** | `string` | Dashes in class names will be camelized, the original class name will be removed from the locals | **file.css** @@ -1287,7 +1287,7 @@ module.exports = { loader: "css-loader", options: { modules: { - exportLocalsConvention: "camelCase", + exportLocalsConvention: "camel-case", }, }, }, diff --git a/src/options.json b/src/options.json index bb61fb12..b8667f03 100644 --- a/src/options.json +++ b/src/options.json @@ -154,10 +154,14 @@ { "enum": [ "asIs", + "as-is", "camelCase", + "camel-case", "camelCaseOnly", + "camel-case-only", "dashes", - "dashesOnly" + "dashesOnly", + "dashes-only" ] }, { diff --git a/src/utils.js b/src/utils.js index 3b434546..f1e32f54 100644 --- a/src/utils.js +++ b/src/utils.js @@ -527,8 +527,8 @@ function getModulesOptions(rawOptions, esModule, exportType, loaderContext) { typeof rawModulesOptions.exportLocalsConvention !== "undefined" ? rawModulesOptions.exportLocalsConvention : namedExport - ? "asIs" - : "camelCaseOnly"; + ? "as-is" + : "camel-case-only"; const modulesOptions = { auto, mode: "local", @@ -555,21 +555,27 @@ function getModulesOptions(rawOptions, esModule, exportType, loaderContext) { if (typeof modulesOptions.exportLocalsConvention === "string") { exportLocalsConventionType = modulesOptions.exportLocalsConvention; - modulesOptions.useExportsAs = exportLocalsConventionType === "asIs"; + modulesOptions.useExportsAs = + exportLocalsConventionType === "as-is" || + exportLocalsConventionType === "asIs"; modulesOptions.exportLocalsConvention = (name) => { switch (exportLocalsConventionType) { + case "camel-case": case "camelCase": { return [name, camelCase(name)]; } + case "camel-case-only": case "camelCaseOnly": { return camelCase(name); } case "dashes": { return [name, dashesCamelCase(name)]; } + case "dashes-only": case "dashesOnly": { return dashesCamelCase(name); } + case "as-is": case "asIs": default: return name; @@ -644,11 +650,14 @@ function getModulesOptions(rawOptions, esModule, exportType, loaderContext) { if ( typeof exportLocalsConventionType === "string" && exportLocalsConventionType !== "asIs" && + exportLocalsConventionType !== "as-is" && exportLocalsConventionType !== "camelCaseOnly" && - exportLocalsConventionType !== "dashesOnly" + exportLocalsConventionType !== "camel-case-only" && + exportLocalsConventionType !== "dashesOnly" && + exportLocalsConventionType !== "dashes-only" ) { throw new Error( - 'The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "asIs", "camelCaseOnly" or "dashesOnly"', + 'The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "as-is", "camel-case-only" or "dashes-only"', ); } } diff --git a/test/__snapshots__/modules-option.test.js.snap b/test/__snapshots__/modules-option.test.js.snap index e86d3615..371e5776 100644 --- a/test/__snapshots__/modules-option.test.js.snap +++ b/test/__snapshots__/modules-option.test.js.snap @@ -2552,7 +2552,7 @@ exports[`"modules" option should throw an error when class has unsupported name exports[`"modules" option should throw an error when the "namedExport" is enabled and the "exportLocalsConvention" options has not "camelCaseOnly" value: errors 1`] = ` [ "ModuleBuildError: Module build failed (from \`replaced original path\`): -Error: The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "asIs", "camelCaseOnly" or "dashesOnly"", +Error: The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "as-is", "camel-case-only" or "dashes-only"", ] `; @@ -2579,7 +2579,7 @@ exports[`"modules" option should throw error when the "exportLocalsConvention" f exports[`"modules" option should throw error with composes when the "namedExport" is enabled and "exportLocalsConvention" options has invalid value: errors 1`] = ` [ "ModuleBuildError: Module build failed (from \`replaced original path\`): -Error: The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "asIs", "camelCaseOnly" or "dashesOnly"", +Error: The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "as-is", "camel-case-only" or "dashes-only"", ] `; @@ -6454,9 +6454,9 @@ a { exports[`"modules" option should work and respect the "localConvention" option with the "asIs" value: warnings 1`] = `[]`; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: errors 1`] = `[]`; +exports[`"modules" option should work and respect the "localConvention" option with the "camel-case-only" value: errors 1`] = `[]`; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: module 1`] = ` +exports[`"modules" option should work and respect the "localConvention" option with the "camel-case-only" value: module 1`] = ` "// Imports import ___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___ from "../../../../src/runtime/noSourceMaps.js"; import ___CSS_LOADER_API_IMPORT___ from "../../../../src/runtime/api.js"; @@ -6483,23 +6483,17 @@ a { } \`, ""]); // Exports -___CSS_LOADER_EXPORT___.locals = { - "foo": \`bar\`, - "my-btn-info_is-disabled": \`value\`, - "myBtnInfoIsDisabled": \`value\`, - "btn-info_is-disabled": \`rmc8ltu8P1VXaeqLNU6N\`, - "btnInfoIsDisabled": \`rmc8ltu8P1VXaeqLNU6N\`, - "btn--info_is-disabled_1": \`AooVHuvzAIGXWngdfslc\`, - "btnInfoIsDisabled1": \`AooVHuvzAIGXWngdfslc\`, - "simple": \`snmJCrfw3LVnrlx87XVC\`, - "foo_bar": \`vA4oeh0XymefKJVIJyg1\`, - "fooBar": \`vA4oeh0XymefKJVIJyg1\` -}; +export var foo = \`bar\`; +export var myBtnInfoIsDisabled = \`value\`; +export var btnInfoIsDisabled = \`rmc8ltu8P1VXaeqLNU6N\`; +export var btnInfoIsDisabled1 = \`AooVHuvzAIGXWngdfslc\`; +export var simple = \`snmJCrfw3LVnrlx87XVC\`; +export var fooBar = \`vA4oeh0XymefKJVIJyg1\`; export default ___CSS_LOADER_EXPORT___; " `; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: result 1`] = ` +exports[`"modules" option should work and respect the "localConvention" option with the "camel-case-only" value: result 1`] = ` [ [ "./modules/localsConvention/localsConvention.css", @@ -6528,11 +6522,11 @@ a { ] `; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: warnings 1`] = `[]`; +exports[`"modules" option should work and respect the "localConvention" option with the "camel-case-only" value: warnings 1`] = `[]`; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCaseOnly" value: errors 1`] = `[]`; +exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: errors 1`] = `[]`; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCaseOnly" value: module 1`] = ` +exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: module 1`] = ` "// Imports import ___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___ from "../../../../src/runtime/noSourceMaps.js"; import ___CSS_LOADER_API_IMPORT___ from "../../../../src/runtime/api.js"; @@ -6561,17 +6555,21 @@ a { // Exports ___CSS_LOADER_EXPORT___.locals = { "foo": \`bar\`, + "my-btn-info_is-disabled": \`value\`, "myBtnInfoIsDisabled": \`value\`, + "btn-info_is-disabled": \`rmc8ltu8P1VXaeqLNU6N\`, "btnInfoIsDisabled": \`rmc8ltu8P1VXaeqLNU6N\`, + "btn--info_is-disabled_1": \`AooVHuvzAIGXWngdfslc\`, "btnInfoIsDisabled1": \`AooVHuvzAIGXWngdfslc\`, "simple": \`snmJCrfw3LVnrlx87XVC\`, + "foo_bar": \`vA4oeh0XymefKJVIJyg1\`, "fooBar": \`vA4oeh0XymefKJVIJyg1\` }; export default ___CSS_LOADER_EXPORT___; " `; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCaseOnly" value: result 1`] = ` +exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: result 1`] = ` [ [ "./modules/localsConvention/localsConvention.css", @@ -6600,7 +6598,7 @@ a { ] `; -exports[`"modules" option should work and respect the "localConvention" option with the "camelCaseOnly" value: warnings 1`] = `[]`; +exports[`"modules" option should work and respect the "localConvention" option with the "camelCase" value: warnings 1`] = `[]`; exports[`"modules" option should work and respect the "localConvention" option with the "dashes" value: errors 1`] = `[]`; diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index e9cc22dc..da19f1c1 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -114,12 +114,12 @@ exports[`validate options should throw an error on the "modules" option with "{" -> Read more at https://github.com/webpack-contrib/css-loader#modules Details: * options.modules.exportLocalsConvention should be one of these: - "asIs" | "camelCase" | "camelCaseOnly" | "dashes" | "dashesOnly" | function + "asIs" | "as-is" | "camelCase" | "camel-case" | "camelCaseOnly" | "camel-case-only" | "dashes" | "dashesOnly" | "dashes-only" | function -> Style of exported classnames. -> Read more at https://github.com/webpack-contrib/css-loader#localsconvention Details: * options.modules.exportLocalsConvention should be one of these: - "asIs" | "camelCase" | "camelCaseOnly" | "dashes" | "dashesOnly" + "asIs" | "as-is" | "camelCase" | "camel-case" | "camelCaseOnly" | "camel-case-only" | "dashes" | "dashesOnly" | "dashes-only" * options.modules.exportLocalsConvention should be an instance of function." `; diff --git a/test/modules-option.test.js b/test/modules-option.test.js index 0654fce8..684bf1cb 100644 --- a/test/modules-option.test.js +++ b/test/modules-option.test.js @@ -1412,14 +1412,13 @@ describe('"modules" option', () => { expect(getErrors(stats)).toMatchSnapshot("errors"); }); - it('should work and respect the "localConvention" option with the "camelCaseOnly" value', async () => { + it('should work and respect the "localConvention" option with the "camel-case-only" value', async () => { const compiler = getCompiler( "./modules/localsConvention/localsConvention.js", { modules: { mode: "local", - exportLocalsConvention: "camelCaseOnly", - namedExport: false, + exportLocalsConvention: "camel-case-only", }, }, ); diff --git a/test/validate-options.test.js b/test/validate-options.test.js index 88ec4f8a..235d250a 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -41,10 +41,13 @@ describe("validate options", () => { { auto: /custom-regex/ }, { auto: () => true }, { exportLocalsConvention: "asIs" }, + { exportLocalsConvention: "as-is" }, { exportLocalsConvention: "camelCase", namedExport: false }, + { exportLocalsConvention: "camel-case", namedExport: false }, { exportLocalsConvention: "camelCaseOnly" }, + { exportLocalsConvention: "camel-case-only" }, { exportLocalsConvention: "dashes", namedExport: false }, - { exportLocalsConvention: "dashesOnly" }, + { exportLocalsConvention: "dashes-only" }, { exportLocalsConvention: (localName) => `${localName.replace(/-/g, "_")}`,