Search code examples
javascriptcsshashwebpackcss-loader

webpack css-loader localIdent name hash length


I use webpack and css-loader, and in my css-loader config I use these options:

options: {
    importLoaders: 1,
    modules: true,
    localIdentName: '[hash:base64:3]'
}

Just like you see, it is obvious that I desire all of my class name will have 3 characters, and after build absolutely my desire come true but there is a very big issue.

Some class names has same name! (conflict!)

for example:

._1mk { /*dev name was .home*/
   color: red;
} /*line 90*/

and

._1mk { /*dev name was .news*/
   color: blue;
}

This is a big issue, but when I use [hash:base64:5] everything would be ok and each class has its own hash name without any conflict.

I search this issue about 4 hours and saw all developers use number 5 as less of length of hash for their config. I don't know why! I calculate that 64 characters [a-z][A-Z][0-9][-,_] in three length can has 262144 different words, so why it can not some different names?

how can I settle this conflict? Really should I miss the number 3 ? and use 5 like others?


Solution

  • Finally, I find the right way, it is hash, not randomNaming function. this is made to hash so it is so obviously in short length with vast naming maybe it produces a collision. so I write my own Webpack naming function and use the variables and the function top on the Webpack config file. these are the steps of my solution:

    At first, two variables for cache and queue. cache for easy accessing to LocalName and its new randomName and queue for holding variable entries that involve all new random names for avoiding a collision.

    let q = [],
      cache = {};
    

    Second, we declare the randomNaming function. I know, maybe it is not very optimized but it works well. the export file is awesome without any collision.

    function randomNaming(length, limit) {
      let result = '',
        chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_',
        /*All valid chars*/
        fchars = 'abcdefghjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_';
      /*All valid first chars*/
    
      do {
        if (q.length >= 52 * Math.pow(64, limit - 1) && limit >= length) {
          return 'OutOfPossibility';
        } else if (q.length >= 52 * Math.pow(64, limit - 1) && limit < length) {
          ++limit;
        }
        result = '';
        result += fchars[Math.floor(Math.random() * fchars.length)];
        for (let i = limit - 1; i > 0; --i) {
          result += chars[Math.floor(Math.random() * chars.length)];
        }
      } while (q.includes(result));
      q.push(result); /*push for avoiding collision in next time of funtion call*/
      return result;
    }
    

    At Third, in css-loader scope inside of webpack config I used getLocalIdent not localIdentName.

    const getLocalIdent = (loaderContext, localIdentName, localName, options) => {
      var randName = randomNaming(3, 2);
      if (localName.match(/^i-/i)) {
        randName = `i-${randName}`;
      } else if (localName.match(/^i_/i)) {
        randName = `i_`;
      } else {
        randName = `${randName}`;
      }
      if (typeof cache[localName] == 'undefined') {
        cache[localName] = randName;
        return cache[localName];
      } else {
        return cache[localName];
      }
    };
    

    And now all of the names are hashed and the CSS file is in minimal possible volume size. And the HTML is so lightweight.