Skip to content

localIdentName setting should only rename classnames for (s)css module files #1307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
martinbroos opened this issue May 11, 2021 · 10 comments
Closed

Comments

@martinbroos
Copy link

martinbroos commented May 11, 2021

  • Operating System: OSX
  • Node Version: 12.18.1
  • NPM Version: 6.14.5
  • webpack Version: 5.36.2
  • css-loader Version: 5.2.4

Expected Behavior

When setting a localIdentName in the modules config it should only rename classnames for (s)css modules.
So files that have .module.(s)css

Actual Behavior

It also renames classnames in scss that are passed as webpack entry. So app.scss has some global css and all of the sudden they get hashed. But the react components have the actual classnames and not a reference to imported styles.

Code

// webpack.config.js

test: /\.s?css$/,
use: [
    {
        loader: require('mini-css-extract-plugin').loader,
    },
    {
        loader: 'css-loader',
        options: {
            sourceMap: true,
            modules: {
                localIdentName:
                    nodeEnv === 'production'
                        ? '[hash:base64]'
                        : '[name]__[local]--[hash:base64:5]',
            },
        }
    },
    {
        loader: 'postcss-loader',
    }
]

How Do We Reproduce?

Create some global css and set this as an entry along with a js entry:

    entry: {
            app: [
                path.resolve(__dirname, 'src/scss/app.scss'),
                path.resolve(__dirname, 'src/js/app.js'),
            ],
    },

and create a css module which you import in the js file.
With the loader settings above you would expect that only the module css would get renamed.

@alexander-akait
Copy link
Member

No, setting:

 modules: {
                localIdentName:
                    nodeEnv === 'production'
                        ? '[hash:base64]'
                        : '[name]__[local]--[hash:base64:5]',
            },

means you enable modules for all classes

@alexander-akait
Copy link
Member

Many developers don't use .module.(s)css and enable modules for all files, so they set modules: true | options, you need two rules if you need handle them separately you can set mode: global and use :local or versa vice

@martinbroos
Copy link
Author

martinbroos commented May 12, 2021

Do you mean that you need to webpack rules with the same css loaders with different settings? Something like this:

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1,
            modules: {
            localIdentName:
                    nodeEnv === 'production'
                        ? '[hash:base64]'
                        : '[name]__[local]--[hash:base64:5]',
            }
          }
        }
      ],
      include: /\.module\.css$/
    },
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader'
      ],
      exclude: /\.module\.css$/
    }
  ]
}

@alexander-akait
Copy link
Member

Yes, but I found it some hacky, we prepare new version, maybe we can improve this, I will think about this, I think we should not consider some options to enable modules

@alexander-akait
Copy link
Member

auto works fine unless you don't need to set options, it is the problem

@martinbroos
Copy link
Author

since the 'auto' option uses a regex to determine if it should use css module logic you would expect that the settings underneath module would only apply on these files.

I did find a workaround maybe this could be integrated.

Add this function to getLocalIdent option under modules

const loaderUtils = require('loader-utils');
const path = require('path');

module.exports = function getLocalIdent(
    context,
    localIdentName,
    localName,
    options
) {
    const filePath = context.resourcePath;
    const fileBaseName = path.basename(filePath);

    // make sure custom classNames are only used for css modules otherwise global css will get changed to but will not match wil html.
    if (/\.module\.scss$/.test(fileBaseName)) {
        // Create a hash based on a the file location and class name. Will be unique across a project, and close to globally unique.
        const hash = loaderUtils.getHashDigest(
            path.posix.relative(context.rootContext, context.resourcePath) +
                localName,
            'md5',
            'base64',
            5
        );

        const className = loaderUtils.interpolateName(
            context,
            '[name]_' + localName + '__' + hash,
            options
        );

        // Remove the .module that appears in every classname when based on the file and replace all "." with "_".
        return className.replace('.module_', '_').replace(/\./g, '_');
    }

    return localName;
};

However you can not fallback make this option undefined again to fallback on regular hashing in case of production environment. So an env check should be added to this function if you want this.

Also i find this solution rather hacky because you need to create the hashes yourself. Maybe you can work with this ?

@alexander-akait
Copy link
Member

It is breaking change and can be changed only for the next major release, I will think

@martinbroos
Copy link
Author

Ok thanks

@alexander-akait
Copy link
Member

We improve our docs for auto https://github.com/webpack-contrib/css-loader/pull/1331/files, shorty: only modules: undeifned and modules: { auto: true } enable auto mode for CSS modules, other values always enable CSS modules, so to fix your problem, you need set:

{
        loader: 'css-loader',
        options: {
            sourceMap: true,
            modules: {
                auto: true,
                localIdentName:
                    nodeEnv === 'production'
                        ? '[hash:base64]'
                        : '[name]__[local]--[hash:base64:5]',
            },
        }
    },

Why? To avoid misleading, when you explicit CSS enable modules, i.e. modules: boolean | string | object we always enable CSS modules, for undefined we check filename, for object we will check auto value to ensure we are allowed to look at filename.

Feel free to feedback.

@martinbroos
Copy link
Author

Yes this works! thanks for looking into this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants