babel-plugin-react-css-modules
babel-plugin-react-css-modules copied to clipboard
Not compatible with css-loader v4
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
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 why you didn't try to create PR for this fix?
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.
@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!
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 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 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 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.
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?
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.
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.
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.
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!
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',
},
],
];
Yeah, I saw that it takes a string or a function, so that's a nice fix! 👏
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
@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.
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!
@gajus could we please get an updated release that is compatible with the latest css-loader? :/
@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.
Upgrade generic-names
to v3.0.0 will fix this rightly
@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.
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)
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 !!!
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 })
解决: 在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',
},
],
@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
解决: 在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', }, ],
老哥 这个稳