0

I can't seem to remove the custom hash on my class names generated by CSS Modules. I'm trying to adjust according to this thread, this medium, this stack overflow, and the web-pack documentation on css-loader but to no success.

I'm using react-app-rewired and attempting the adjust the webpack configuration modeling myself after react-app-rewire-typings-for-css-module and this thread which has worked well for converting to camelCase.

I'm attempting to use the well-documented localIdentName and/or getLocalIdent but can't seem to get the correct configuration. The syntax continues to follow [name]__[local]__[hash:base64:5]

config-override.js

/* config-overrides.js */
// @link https://github.com/timarney/react-app-rewired/


module.exports = {

    webpack: (config) => {

        const regexes = [
            /\.module\.css$/.toString(),
            /\.module\.(scss|sass)$/.toString()
        ];

        const oneOfs = config.module.rules.find((rule) => !!rule.oneOf).oneOf;

        // @link https://webpack.js.org/loaders/css-loader/
        // camel-case style names from css modules
        for (const oneOf of oneOfs) {
            if (!oneOf.test || !regexes.includes(oneOf.test.toString())) continue;

            const cssLoader = oneOf.use.find(
                (entry) =>
                    entry.loader &&
                    entry.loader.includes('css-loader') &&
                    !entry.loader.includes('postcss-loader')
            );

            const {options} = cssLoader

            if (undefined !== options && options.modules) {
                // @link https://webpack.js.org/loaders/css-loader/
                options.modules.exportLocalsConvention = 'camelCase';
                options.modules.localIdentName = '[local]';
                options.localIdentName = '[local]';
                options.modules.exportGlobals = true;
                options.modules.getLocalIdent = (context, localIdentName, localName, options) => {
                    console.log(localIdentName)
                    return localIdentName;
                };

            }

        }

        return config;

    }

};

My dependencies in package.json

"dependencies": {
    "@carbon/react": "^1.9.0",
    "@fortawesome/fontawesome-free": "^6.1.2",
    "@fortawesome/fontawesome-svg-core": "^6.1.2",
    "@fortawesome/free-solid-svg-icons": "^6.1.2",
    "@fortawesome/react-fontawesome": "^0.2.0",
    "@sweetalert/with-react": "^0.1.1",
    "@types/history": "^4.7.2",
    "@types/jest": "24.0.15",
    "@types/react-dom": "^18.0.0",
    "animate.css": "^3.7.2",
    "axios": "^0.24.0",
    "axios-cache-adapter": "^2.7.3",
    "bootstrap": "5.2.0",
    "canvas-nest.js": "^2.0.4",
    "chartist": "0.10.1",
    "classnames": "^2.3.1",
    "colors": "^1.3.3",
    "core-js": "^3.6.5",
    "css-loader": "^6.7.1",
    "dangerously-set-html-content": "^1.0.9",
    "deepmerge": "^4.2.2",
    "dropzone": "^5.7.1",
    "formik": "^2.2.9",
    "history": "^4.9.0",
    "ip": "^2.0.0",
    "jquery": "^3.6.0",
    "jquery-backstretch": "2.1.16",
    "jquery-bracket": "^0.11.1",
    "jquery-form": "^4.3.0",
    "jquery-pjax": "^2.0.1",
    "jsoneditor": "^9.5.6",
    "moment": "^2.29.3",
    "moment-countdown": "^0.0.3",
    "moment-timezone": "^0.5.34",
    "mustache": "^2.3.2",
    "nouislider": "13.1.5",
    "npm-run-all": "4.1.3",
    "pace-js": "^1.2.4",
    "perfect-scrollbar": "1.4.0",
    "php-serialized-data": "^0.6.1",
    "prop-types": "15.7.2",
    "qs": "^6.9.4",
    "raw.macro": "^0.4.2",
    "react": "^18.0.0",
    "react-animate-on-scroll": "^2.1.5",
    "react-big-calendar": "0.20.1",
    "react-chartist": "0.13.3",
    "react-code-blocks": "0.0.8",
    "react-data-grid": "^7.0.0-beta.13",
    "react-data-table-component": "^7.5.0",
    "react-datetime": "^3.1.1",
    "react-dom": "^18.0.0",
    "react-helmet": "^6.1.0",
    "react-jvectormap": "0.0.15",
    "react-loading-skeleton": "^3.0.1",
    "react-outside-click-handler": "^1.3.0",
    "react-render-html": "^0.6.0",
    "react-responsive": "^8.2.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "5.0.1",
    "react-select": "^5.3.1",
    "react-slick": "^0.24.0",
    "react-table": "^7.7.0",
    "react-tagsinput": "^3.19.0",
    "react-toastify": "^8.1.0",
    "simplex-noise": "^3.0.0",
    "sweetalert": "^2.1.2",
    "three-js": "^79.0.0",
    "ts-node": "^10.7.0",
    "typed-scss-modules": "^6.5.0",
    "web-vitals": "^2.1.4",
    "yup": "^0.32.11"
  },

Please let me know if you see any problems or need more information! Thanks in advance

EDIT:

I'm trying to gain additional context using const log = getLogger({ name: 'config-overrides.js' }); and after adding have received a new, seemingly unrelated, message

ERROR in ./src/views/Tournaments/style.module.scss (./node_modules/css-modules-typescript-loader/index.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].oneOf[8].use[2]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].oneOf[8].use[3]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[0].oneOf[8].use[4]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[0].oneOf[8].use[5]!./src/views/Tournaments/style.module.scss)
Module build failed (from ./node_modules/css-loader/dist/cjs.js):
Error: The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "camelCaseOnly" or "dashesOnly"
    at getModulesOptions (/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/utils.js:635:13)
    at normalizeOptions (/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/utils.js:644:26)
    at Object.loader (/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/index.js:37:43)

And I'm also using this configuration. After running I'm caught in what seems like an infinite boot.

/* config-overrides.js */
// @link https://github.com/timarney/react-app-rewired/
const getLogger = require('webpack-log');
const rewireTypingsForCssModule = require("react-app-rewire-typings-for-css-module");

const log = getLogger({ name: 'config-overrides.js' });

module.exports = {

    webpack: (config) => {

        config = rewireTypingsForCssModule.factory({
            esModule: true,
            modules: {
                exportLocalsConvention: 'camelCase',
                localIdentName: '[local]',
                exportGlobals: true,
                getLocalIdent: (context, localIdentName, localName, options) => {
                    //log.info('info', context)
                    log.warn('localIdentName', localIdentName)
                    log.trace('localName', localName)
                    log.error('options', options)
                    return localName;
                }
            }
        })(config);

        return config;

    }

};

After letting this configuration run into failure, a heap exhaust was reached. Commenting out this log.info('info', context) will solve the issue.

<--- Last few GCs --->

[5654:0x7fdcff12b000]   196783 ms: Mark-sweep (reduce) 4069.0 (4143.6) -> 4068.1 (4143.9) MB, 5176.7 / 0.0 ms  (average mu = 0.084, current mu = 0.002) allocation failure; scavenge might not succeed
[5654:0x7fdcff12b000]   201782 ms: Mark-sweep (reduce) 4069.3 (4143.9) -> 4068.4 (4144.1) MB, 4989.4 / 0.0 ms  (average mu = 0.044, current mu = 0.002) allocation failure; scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
 1: 0x10da9b4a8 node::Abort() [/usr/local/Cellar/node/18.7.0/bin/node]
 2: 0x10da9c619 node::OnFatalError(char const*, char const*) [/usr/local/Cellar/node/18.7.0/bin/node]
 3: 0x10dbf524d v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/Cellar/node/18.7.0/bin/node]
 4: 0x10dbf51e5 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/Cellar/node/18.7.0/bin/node]
 5: 0x10dd51097 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/usr/local/Cellar/node/18.7.0/bin/node]
 6: 0x10dd4fcca v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/Cellar/node/18.7.0/bin/node]
 7: 0x10dd44d5d v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/Cellar/node/18.7.0/bin/node]
 8: 0x10dd4557d v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/Cellar/node/18.7.0/bin/node]
 9: 0x10dd2eecb v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/local/Cellar/node/18.7.0/bin/node]
10: 0x10e0251b2 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/local/Cellar/node/18.7.0/bin/node]
11: 0x10d8f58b9 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/usr/local/Cellar/node/18.7.0/bin/node]

After about 5-10 iterations, I've stopped receiving logging from the console. Specifically the getLocalIdent function. Below is my base-line webpack configuration which is still logging.

⬡ config-overrides.js: webpack configuration start {"target":["browserslist"],"stats":"errors-warnings","mode":"development","bail":false,"devtool":"cheap-module-source-map","entry":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/src/index.tsx","output":{"path":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/build","pathinfo":true,"filename":"static/js/bundle.js","chunkFilename":"static/js/[name].chunk.js","assetModuleFilename":"static/media/[name].[hash][ext]","publicPath":"/"},"cache":{"type":"filesystem","version":"2173f3a0c48bf394b767b880b6f1a1c1","cacheDirectory":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/.cache","store":"pack","buildDependencies":{"defaultWebpack":["webpack/lib/"],"config":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/react-scripts/config/webpack.config.js"],"tsconfig":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/tsconfig.json"]}},"infrastructureLogging":{"level":"none"},"optimization":{"minimize":false,"minimizer":[{"options":{"test":{},"extractComments":true,"parallel":true,"minimizer":{"options":{"parse":{"ecma":8},"compress":{"ecma":5,"warnings":false,"comparisons":false,"inline":2},"mangle":{"safari10":true},"keep_classnames":false,"keep_fnames":false,"output":{"ecma":5,"comments":false,"ascii_only":true}}}}},{"options":{"test":{},"parallel":true,"minimizer":{"options":{}}}}]},"resolve":{"modules":["node_modules","/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules"],"extensions":[".web.mjs",".mjs",".web.js",".js",".web.ts",".ts",".web.tsx",".tsx",".json",".web.jsx",".jsx"],"alias":{"react-native":"react-native-web","src":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/src"},"plugins":[{"appSrcs":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/src"],"allowedFiles":{},"allowedPaths":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/react-refresh","/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/@pmmmwh/react-refresh-webpack-plugin/lib","/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/babel-preset-react-app","/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/@babel/runtime/helpers/esm","/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/@babel/runtime/regenerator"]}]},"module":{"strictExportPresence":true,"rules":[{"oneOf":[{"test":[{}],"type":"asset","mimetype":"image/avif","parser":{"dataUrlCondition":{"maxSize":10000}}},{"test":[{},{},{},{}],"type":"asset","parser":{"dataUrlCondition":{"maxSize":10000}}},{"test":{},"use":[{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/@svgr/webpack/lib/index.js","options":{"prettier":false,"svgo":false,"svgoConfig":{"plugins":[{"removeViewBox":false}]},"titleProp":true,"ref":true}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/file-loader/dist/cjs.js","options":{"name":"static/media/[name].[hash].[ext]"}}],"issuer":{"and":[{}]}},{"test":{},"include":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/src","loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/babel-loader/lib/index.js","options":{"customize":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/babel-preset-react-app/webpack-overrides.js","presets":[["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/babel-preset-react-app/index.js",{"runtime":"automatic"}]],"babelrc":false,"configFile":false,"cacheIdentifier":"development:[email protected]:[email protected]:[email protected]:[email protected]","plugins":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/react-refresh/babel.js"],"cacheDirectory":true,"cacheCompression":false,"compact":false}},{"test":{},"exclude":{},"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/babel-loader/lib/index.js","options":{"babelrc":false,"configFile":false,"compact":false,"presets":[["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/babel-preset-react-app/dependencies.js",{"helpers":true}]],"cacheDirectory":true,"cacheCompression":false,"cacheIdentifier":"development:[email protected]:[email protected]:[email protected]:[email protected]","sourceMaps":false,"inputSourceMap":false}},{"test":{},"exclude":{},"use":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/style-loader/dist/cjs.js",{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/cjs.js","options":{"importLoaders":1,"sourceMap":true,"modules":{"mode":"icss"}}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/postcss-loader/dist/cjs.js","options":{"postcssOptions":{"ident":"postcss","config":false,"plugins":["postcss-flexbugs-fixes",["postcss-preset-env",{"autoprefixer":{"flexbox":"no-2009"},"stage":3}],"postcss-normalize"]},"sourceMap":true}}],"sideEffects":true},{"test":{},"use":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/style-loader/dist/cjs.js",{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/cjs.js","options":{"importLoaders":1,"sourceMap":true,"modules":{"mode":"local"}}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/postcss-loader/dist/cjs.js","options":{"postcssOptions":{"ident":"postcss","config":false,"plugins":["postcss-flexbugs-fixes",["postcss-preset-env",{"autoprefixer":{"flexbox":"no-2009"},"stage":3}],"postcss-normalize"]},"sourceMap":true}}]},{"test":{},"exclude":{},"use":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/style-loader/dist/cjs.js",{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/cjs.js","options":{"importLoaders":3,"sourceMap":true,"modules":{"mode":"icss"}}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/postcss-loader/dist/cjs.js","options":{"postcssOptions":{"ident":"postcss","config":false,"plugins":["postcss-flexbugs-fixes",["postcss-preset-env",{"autoprefixer":{"flexbox":"no-2009"},"stage":3}],"postcss-normalize"]},"sourceMap":true}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/resolve-url-loader/index.js","options":{"sourceMap":true,"root":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/src"}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/sass-loader/dist/cjs.js","options":{"sourceMap":true}}],"sideEffects":true},{"test":{},"use":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/style-loader/dist/cjs.js",{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/css-loader/dist/cjs.js","options":{"importLoaders":3,"sourceMap":true,"modules":{"mode":"local"}}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/postcss-loader/dist/cjs.js","options":{"postcssOptions":{"ident":"postcss","config":false,"plugins":["postcss-flexbugs-fixes",["postcss-preset-env",{"autoprefixer":{"flexbox":"no-2009"},"stage":3}],"postcss-normalize"]},"sourceMap":true}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/resolve-url-loader/index.js","options":{"sourceMap":true,"root":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/src"}},{"loader":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/sass-loader/dist/cjs.js","options":{"sourceMap":true}}]},{"exclude":[{},{},{},{}],"type":"asset/resource"}]}]},"plugins":[{"userOptions":{"inject":true,"template":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/public/index.html"},"version":5},{"replacements":{"NODE_ENV":"development","PUBLIC_URL":"","FAST_REFRESH":true,"REACT_APP_NODE_PROXY_URL":"http://local.dropingaming.gg:8080/","REACT_APP_NODE_PROXY_WS_URL_TARGET":"ws://local.dropingaming.gg:8888/","REACT_APP_TAIS_ENV":"1"}},{"appPath":"/Users/richardmiles/IdeaProjects/dropingaming.com/react"},{"definitions":{"process.env":{"NODE_ENV":"\"development\"","PUBLIC_URL":"\"\"","FAST_REFRESH":"true","REACT_APP_NODE_PROXY_URL":"\"http://local.dropingaming.gg:8080/\"","REACT_APP_NODE_PROXY_WS_URL_TARGET":"\"ws://local.dropingaming.gg:8888/\"","REACT_APP_TAIS_ENV":"\"1\""}}},{"options":{"overlay":false,"exclude":{},"include":{}}},{"options":{},"logger":{},"pathCache":{},"fsOperations":0,"primed":false},{"options":{"assetHookStage":null,"basePath":"","fileName":"asset-manifest.json","filter":null,"map":null,"publicPath":"/","removeKeyHash":{},"sort":null,"transformExtensions":{},"useEntryKeys":false,"useLegacyEmit":false,"writeToFileEmit":false}},{"options":{"resourceRegExp":{},"contextRegExp":{}}},{"options":{"async":true,"typescript":{"typescriptPath":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/typescript/lib/typescript.js","configOverwrite":{"compilerOptions":{"sourceMap":true,"skipLibCheck":true,"inlineSourceMap":false,"declarationMap":false,"noEmit":true,"incremental":true,"tsBuildInfoFile":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/.cache/tsconfig.tsbuildinfo"}},"context":"/Users/richardmiles/IdeaProjects/dropingaming.com/react","diagnosticOptions":{"syntactic":true},"mode":"write-references"},"issue":{"include":[{"file":"../**/src/**/*.{ts,tsx}"},{"file":"**/src/**/*.{ts,tsx}"}],"exclude":[{"file":"**/src/**/__tests__/**"},{"file":"**/src/**/?(*.){spec|test}.*"},{"file":"**/src/setupProxy.*"},{"file":"**/src/setupTests.*"}]},"logger":{"infrastructure":"silent"}}},{"key":"ESLintWebpackPlugin","options":{"extensions":["js","mjs","jsx","ts","tsx"],"emitError":true,"emitWarning":true,"failOnError":true,"resourceQueryExclude":[],"formatter":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/react-dev-utils/eslintFormatter.js","eslintPath":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/react-scripts/node_modules/eslint/lib/api.js","context":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/src","cache":true,"cacheLocation":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/.cache/.eslintcache","cwd":"/Users/richardmiles/IdeaProjects/dropingaming.com/react","resolvePluginsRelativeTo":"/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/react-scripts/config","baseConfig":{"extends":["/Users/richardmiles/IdeaProjects/dropingaming.com/react/node_modules/eslint-config-react-app/base.js"],"rules":{}}}}],"performance":false}

Okay so Webpack will cache these generated configurations; you can run rm -rf node_modules/.cache to get the logging to reset. This seems to not be perfect either..

Nearing Solution:

That seems to, for the first time, actually effect the page. The problem now is how explicit it is. My output in the browser now looks like class="[local] [local] [local] [local] [local]". This is encouraging.

1 Answer 1

0

Webpack aggressively caches these directives and must be cleared for changes to be seen. I've had some success with running rm -rf node_modules/.cache before I npm start. Generally the code above is good, but verbose. It can be simplified to the following:

/* config-overrides.js */
// @link https://github.com/timarney/react-app-rewired/
const getLogger = require('webpack-log');
const rewireTypingsForCssModule = require("react-app-rewire-typings-for-css-module");

const log = getLogger({ name: 'config-overrides.js' });

module.exports = {

    webpack: (config) => {

        // what's going on here? @link https://stackoverflow.com/questions/73551420/removing-hash-from-react-css-modules
        config = rewireTypingsForCssModule.factory({
            modules: {
                exportLocalsConvention: 'camelCase',
                mode: 'local',
                exportGlobals: true,
                getLocalIdent: (context, localIdentName, localName) => {
                    return localName;
                }
            }
        })(config);

        log.info('webpack configuration post config-overrides.js', JSON.stringify(config))

        return config;

    }

};
Sign up to request clarification or add additional context in comments.

1 Comment

Adding the logging, seen in the question above, is the best way to know if the cache was re-filled or not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.