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:babel-plugin-named-asset-import@0.3.8:babel-preset-react-app@10.0.1:react-dev-utils@12.0.1:react-scripts@5.0.1","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:babel-plugin-named-asset-import@0.3.8:babel-preset-react-app@10.0.1:react-dev-utils@12.0.1:react-scripts@5.0.1","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.
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;
}
};