gajus / babel-plugin-react-css-modules

Transforms styleName to className using compile time CSS module resolution.
Other
2.05k stars 162 forks source link

Not compatible with css-loader v4 #291

Open fracmak opened 3 years ago

fracmak commented 3 years ago

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

birdofpreyru commented 3 years ago

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

epotockiy commented 3 years ago

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

birdofpreyru commented 3 years ago

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.

bhj commented 3 years ago

@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!

Hless commented 3 years ago

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?

birdofpreyru commented 3 years ago

@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 :)

Hless commented 3 years ago

@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

birdofpreyru commented 3 years ago

@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.

Hless commented 3 years ago

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?

birdofpreyru commented 3 years ago

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.

benmvp commented 3 years ago

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.

birdofpreyru commented 3 years ago

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.

benmvp commented 3 years ago

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!

fracmak commented 3 years ago

The way I got around it was by importing loader-utils@2.0.0, 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',
        },
      ],
    ];
benmvp commented 3 years ago

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

fracmak commented 3 years ago

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

benmvp commented 3 years ago

@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.

zycoJamie commented 3 years ago

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!

ThiefMaster commented 3 years ago

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

gajus commented 3 years ago

@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.

shallinta commented 3 years ago

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

PaulSearcy commented 3 years ago

@gajus, @shallinta this isn't going to be as simple as npm i generic-names@3.0.0 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.

dr2009 commented 3 years ago
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)
eleven-net-cn commented 3 years ago
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 !!!

0es commented 3 years ago

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 })
zsjun commented 2 years ago

解决: 在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',

},

],
mbonaci commented 2 years ago

@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

owencyc commented 2 years ago

解决: 在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',

},

],

老哥 这个稳