babel-plugin-react-css-modules icon indicating copy to clipboard operation
babel-plugin-react-css-modules copied to clipboard

Not compatible with css-loader v4

Open fracmak opened this issue 4 years ago • 28 comments

It appears the new css-loader has changed their hash algorithm to use md4 instead of md5 (related to https://github.com/webpack/loader-utils/pull/168 ), this means the hash generated by this babel plugin no longer matches what css-loader's hash is with no clear way around it

fracmak avatar Aug 03 '20 21:08 fracmak

Here is my fork of the plugin, with fixed css-loader compatibility: https://www.npmjs.com/package/@dr.pogodin/babel-plugin-react-css-modules

birdofpreyru avatar Aug 08 '20 22:08 birdofpreyru

@birdofpreyru why you didn't try to create PR for this fix?

epotockiy avatar Aug 26 '20 10:08 epotockiy

To move fast. It looks like the author does not maintain the package actively for a long time. I don't want to wait for review by the author, don't really want to discuss updates of dependencies I like to do, etc. Once I got it working, it took me no time to setup release as a separate package, and now if I need any other corrections I can do them fast.

birdofpreyru avatar Aug 26 '20 10:08 birdofpreyru

@gajus mind taking a look? This can be a whale of an issue to troubleshoot if one isn't aware of the changes OP mentions. And thank you for this awesome library!

bhj avatar Aug 31 '20 13:08 bhj

Pfew, lifesaver this one.

Anyway, what are the plans to maintain that fork? Do you aim for it to replace this plugin at some point?

Hless avatar Sep 22 '20 14:09 Hless

@Hless do you ask me? I use my fork in my React projects, thus in the foreseen future I am planning to maintain it functional and up-to-date. At the same time, I don't have any issues with what it does and how it works, thus probably won't do with it anything beyond ocasional dependency updates when something breaks for me, or somebody asks for it.

Everybody else following the thread: it is almost two months since the issue ticket was created, since no reply from the repo owner. I hope you see now my choice between fork and PR was very reasonable :)

birdofpreyru avatar Sep 22 '20 16:09 birdofpreyru

@birdofpreyru I suppose you're right about the fork, repo does not seem actively maintained anymore, thanks for your work.

I decided to refactor my codebase not use the styleName prop anymore, but to refactor it to the standard import/className workflow. Webpack config is hard enough to maintain without having to rely upon plugins that are not actively maintained by a larger audience

Hless avatar Sep 29 '20 10:09 Hless

@Hless What is your plan regarding CSS class name scoping? styleName prop itself is not a big deal.

From my seat, CSS modules worked fine for me for three years without any maintenance, thus it feels like spending a few days now to fix the compatibility with the latest PostCSS will make it a smooth ride for next few years.

birdofpreyru avatar Sep 29 '20 11:09 birdofpreyru

Well I did some digging and unless I'm mistaken the CSS module feature is part of css-loader now. So if you were to use react scripts you can see the documentation here: https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/

Since I'm using a custom webpack config, I configured it using css-loader directly:

{
        loader: require.resolve("css-loader"),
        options:{
               modules: {
                  localIdentName: "[name]__[local]__[hash:base64:5]"
                },
        },
}, 

(Note: this is the v4 syntax)

Documentation on css-loader is here: https://webpack.js.org/loaders/css-loader/#localidentname

I could be completely missing something here, as of now the only reason for using this babel plugin would be the styleName prop?

Hless avatar Sep 29 '20 11:09 Hless

It is a good point! I am also relying on this and another css-modules Babel plugins because in my setup I compile server-side version of code only with Babel, without Webpack; but your message makes me think that probably I should revisit how I do stuff, and indeed simplify it relying only on css-loader for style manipulations, and finding a better way, less dependent on the actual CSS transformations, to deel with styleName props (don't really want to abandon them). Anyway, in the closest future I intent to maintain my fork of this plugin, and maybe then come up with a full replacement which does not depend on that much stuff without active maintenance by a community.

birdofpreyru avatar Sep 30 '20 12:09 birdofpreyru

So I've spent a few days trying to figure this out. 😅 The problem is a change in css-loader from this PR.

I've opened an issue here: https://github.com/webpack-contrib/css-loader/issues/1214

The tl;dr is that the PR changed + to \x00 in the content it hashes, so the hashes generated by babel-plugin-react-css-modules (from here) is different than the one generated by css-loader (from here). That's why no matter how I tried to change how the content was hashed, the class names in the markup never matched those in the generated CSS.

benmvp avatar Oct 22 '20 01:10 benmvp

Good job @benmvp , though it was figured out a while ago: https://github.com/webpack-contrib/css-loader/issues/1152 and in the beginning of thread you have a link to my fork of the package which is patched to match the latest css-loader implementation, and also to rely on latest versions of all dependencies.

birdofpreyru avatar Oct 22 '20 09:10 birdofpreyru

Good job @benmvp , though it was figured out a while ago: webpack-contrib/css-loader#1152 and in the beginning of thread you have a link to my fork of the package which is patched to match the latest css-loader implementation, and also to rely on latest versions of all dependencies.

Yeah @birdofpreyru I realized after doing the research and filing the bug that I should've just looked more closely at your fork to see what the problem was 🤦 Thanks for the links!

benmvp avatar Oct 22 '20 16:10 benmvp

The way I got around it was by importing [email protected], and then adding this to my babel.config.js

const { interpolateName } = require('loader-utils');

function generateScopedName(pattern) {
  const context = process.cwd();
  return function generate(localName, filepath) {
    const name = pattern.replace(/\[local\]/gi, localName);
    const loaderContext = {
      resourcePath: filepath,
    };

    const loaderOptions = {
      content: `${path.relative(context, filepath).replace(/\\/g, '/')}\u0000${localName}`,
      context,
    };

    const genericName = interpolateName(loaderContext, name, loaderOptions);
    return genericName
      .replace(new RegExp('[^a-zA-Z0-9\\-_\u00A0-\uFFFF]', 'g'), '-')
      .replace(/^((-?[0-9])|--)/, '_$1');
  };
}


const plugins = [
      [
        'babel-plugin-react-css-modules',
        {
          generateScopedName: generateScopedName('[name]__[local]_[hash:base64:5]'),
          exclude: 'node_modules',
        },
      ],
    ];

fracmak avatar Oct 22 '20 16:10 fracmak

Yeah, I saw that it takes a string or a function, so that's a nice fix! 👏

benmvp avatar Oct 22 '20 17:10 benmvp

Ya, since I mainly copied code that already existed in these underlying libraries, it would be nice if they were exposed so we could just flag on which behavior we wanted

fracmak avatar Oct 22 '20 17:10 fracmak

@birdofpreyru - I've got a PR open (https://github.com/css-modules/generic-names/pull/10) in generic-names to update to match css-loader. With it merged and released, it should make fixing the bug here in babel-plugin-react-css-modules as simple as bumping the dependency.

Hopefully we can get that merged here, but at the very least it should make maintaining the fix easier in your fork.

benmvp avatar Oct 23 '20 18:10 benmvp

Here is my fork of the plugin, with fixed css-loader compatibility: https://www.npmjs.com/package/@dr.pogodin/babel-plugin-react-css-modules

nice,hash has matched css-loader,thank you!

zycoJamie avatar Nov 20 '20 04:11 zycoJamie

@gajus could we please get an updated release that is compatible with the latest css-loader? :/

ThiefMaster avatar Nov 25 '20 18:11 ThiefMaster

@gajus could we please get an updated release that is compatible with the latest css-loader? :/

If someone raises a PR, I will happily review it and integrate it.

gajus avatar Nov 26 '20 17:11 gajus

Upgrade generic-names to v3.0.0 will fix this rightly

shallinta avatar Dec 15 '20 12:12 shallinta

@gajus, @shallinta this isn't going to be as simple as npm i [email protected] and then making PR.

Initially cloned, installed packages and then ran the tests. 8/29 failed

Then I realized there is no package-lock.json and ^ (minor + patch) range is used for most dependencies. This means tests are going to pass or fail based on when the project was cloned and what versions the dependencies resolved to.

Edit

Read the whole thread and just dawned on me that @birdofpreyru also updated all the dependencies in his fork. If he would be kind enough to make a PR back here? For now I'm going to use his fork to keep the project I'm on moving.

PaulSearcy avatar Dec 15 '20 19:12 PaulSearcy

const genericNames = require('generic-names'); // v3.0.0
const CSS_MODULE_LOCAL_IDENT_NAME = '[local]___[hash:base64:5]';
// old:  generateScopedName: CSS_MODULE_LOCAL_IDENT_NAME
generateScopedName: genericNames(CSS_MODULE_LOCAL_IDENT_NAME)

dr2009 avatar Dec 17 '20 01:12 dr2009

const genericNames = require('generic-names'); // v3.0.0
const CSS_MODULE_LOCAL_IDENT_NAME = '[local]___[hash:base64:5]';
// old:  generateScopedName: CSS_MODULE_LOCAL_IDENT_NAME
generateScopedName: genericNames(CSS_MODULE_LOCAL_IDENT_NAME)

Nice !!!

eleven-net-cn avatar Jan 05 '21 03:01 eleven-net-cn

with another hash conversion problem, try to pass context to genericNames

const genericNames = require('generic-names'); // v3.0.0
const CSS_MODULE_LOCAL_IDENT_NAME = '[local]___[hash:base64:5]';
// old:  generateScopedName: CSS_MODULE_LOCAL_IDENT_NAME
generateScopedName: genericNames(CSS_MODULE_LOCAL_IDENT_NAME)
generateScopedName: genericNames(CSS_MODULE_LOCAL_IDENT_NAME, { context })

0es avatar Jan 12 '21 08:01 0es

解决: 在webpack.config.js中使用"generic-names": "^3.0.0", 生成类名,在babel.config.js中使用生成类名,也就是通过generic-names让两者达成一致。

webpack.config.js 配置如下:

const genericNames = require('generic-names');
const generateScope = genericNames(localIdentName, {

context: process.cwd(),

});
const getStyleLoaders = (cssOptions, preProcessor = []) => {

const loaders = [

// require.resolve('style-loader'),

MiniCssExtractPlugin.loader,

// isProd ? MiniCssExtractPlugin.loader : require.resolve('style-loader'),

{

loader: require.resolve('css-loader'),

options: cssOptions,

},

];

if (preProcessor.length > 0) {

for (let item of preProcessor) {

loaders.push(require.resolve(item));

}

}

return loaders;

};
{

test: /\.scss$/,

exclude: /node_modules/,

include: path.resolve(__dirname, 'src'),

use: getStyleLoaders(

{

importLoaders: 1,

// modules: {

// localIdentName: '[name]__[local]_[hash:base64:5]',

// localIdentContext: path.resolve(__dirname, 'src'),

// },

modules: {

getLocalIdent({ resourcePath }, localIdentName, localName) {

return generateScope(localName, resourcePath);

},

},

},

['sass-loader']

),

},

babel.config.js 配置如下:

const genericNames = require('generic-names'); // v3.0.0
// babel-plugin-react-css-modules

[

'react-css-modules',

{

generateScopedName: genericNames('[name]__[local]_[hash:base64:5]'),

filetypes: {

'.scss': {

syntax: 'postcss-scss',

},

},

exclude: 'node_modules',

},

],

zsjun avatar Sep 02 '21 03:09 zsjun

@Hless

the CSS module feature is part of css-loader now... as of now the only reason for using this babel plugin would be the styleName prop?

AFAIK this was always the case

mbonaci avatar Oct 31 '21 10:10 mbonaci

解决: 在webpack.config.js中使用"generic-names": "^3.0.0", 生成类名,在babel.config.js中使用生成类名,也就是通过generic-names让两者达成一致。

webpack.config.js 配置如下:

const genericNames = require('generic-names');
const generateScope = genericNames(localIdentName, {

context: process.cwd(),

});
const getStyleLoaders = (cssOptions, preProcessor = []) => {

const loaders = [

// require.resolve('style-loader'),

MiniCssExtractPlugin.loader,

// isProd ? MiniCssExtractPlugin.loader : require.resolve('style-loader'),

{

loader: require.resolve('css-loader'),

options: cssOptions,

},

];

if (preProcessor.length > 0) {

for (let item of preProcessor) {

loaders.push(require.resolve(item));

}

}

return loaders;

};
{

test: /\.scss$/,

exclude: /node_modules/,

include: path.resolve(__dirname, 'src'),

use: getStyleLoaders(

{

importLoaders: 1,

// modules: {

// localIdentName: '[name]__[local]_[hash:base64:5]',

// localIdentContext: path.resolve(__dirname, 'src'),

// },

modules: {

getLocalIdent({ resourcePath }, localIdentName, localName) {

return generateScope(localName, resourcePath);

},

},

},

['sass-loader']

),

},

babel.config.js 配置如下:

const genericNames = require('generic-names'); // v3.0.0
// babel-plugin-react-css-modules

[

'react-css-modules',

{

generateScopedName: genericNames('[name]__[local]_[hash:base64:5]'),

filetypes: {

'.scss': {

syntax: 'postcss-scss',

},

},

exclude: 'node_modules',

},

],

老哥 这个稳

owencyc avatar Apr 13 '22 05:04 owencyc