Obfuscate React classNames in existing codebase

Viewed 2842

Hi guys

I am working on a big React application with an existing code-base (100+ components). Currently, we are using the traditional styling method, example:

JSX:
<div className="div" />

SCSS: 
.div {
 /* ... */
}

We are using webpack with these loaders:

/* ... */
use: [
       {
         loader: MiniCssExtractPlugin.loader,
         options: {
           publicPath: '../'
         }
       },
       'css-loader',
       'postcss-loader',
       {
         loader: 'sass-loader',
         options: { implementation: sass }
       }
     ]
/* ... */

Is there any way to obfuscate these classNames from both JSX and SCSS without rewriting the whole thing? Is it also possible to rewrite only some components to be obfuscated or do we have to rewrite it all?

Thanks.

2 Answers

css-loader has a modules option which when set to true obfuscates class names. Unfortunately, this will obfuscate all of the class names and will require you to change all usages of <div className="div" /> to:

import styles from <stylesheet>;
<div className={styles.div} />

Edit: The only way I can think of avoiding having to change all of the usages at once is by splitting your css config in webpack with two patterns, for example:

{
    test: /.*dirA\/.*\.css/,
    use: [{ loader: 'style-loader' }, { loader: 'css-loader', options: { modules: true } }, ...]
},
{
    test: /.*dirB\/.*\.css/,
    use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, ...]
}

Solution for ejected Create-react-app

Example classNames: a_a, a_b, a_c .... etc.

  1. Eject app
  2. Install incstr
  3. Create getScopedName.js at the config folder.
const incstr = require('incstr');
const createUniqueIdGenerator = () => {
  const uniqIds = {};
  const generateNextId = incstr.idGenerator({
    alphabet: 'abcefghijklmnopqrstuvwxyzABCEFGHJKLMNOPQRSTUVWXYZ',
  });
  return (name) => {
    if (!uniqIds[name]) {
      uniqIds[name] = generateNextId();
    }
    return uniqIds[name];
  };
};
const localNameIdGenerator = createUniqueIdGenerator();
const componentNameIdGenerator = createUniqueIdGenerator();
module.exports = (localName, resourcePath) => {
  const componentName = resourcePath
    .split('/')
    .slice(-2, -1)[0];
  const localId = localNameIdGenerator(localName);
  const componentId = componentNameIdGenerator(componentName);
  return `${componentId}_${localId}`;
};

Now...

  1. Open the webpack.config.js and add:
const getScopedName = require('./getScopedName')
  1. Find the ~445-460 rows and replace with:
{
  test: cssModuleRegex,
  use: getStyleLoaders({
    importLoaders: 1,
    sourceMap: isEnvProduction && shouldUseSourceMap,
    modules: {
      ...(isEnvDevelopment ? {
        localIdentName: '[path]_[name]_[local]',
      } : {
       getLocalIdent: (context, localIdentName, localName) => (
         getScopedName(localName, context.resourcePath)
       ),
      })
     },
  }),
}

It's all. :)

Related