Using css modules with Extract Text Plugin
Asked Answered
K

1

12

Webpack 2 build doesn't work as expected in production mode using css modules option in css-loader with extract-text-webpack-plugin.

the correct generated classes are created on html elements, implying that css-loader is working as intended, but the extracted css file from the extract-text-webpack-plugin is lacking the css identifiers.

I'm using a method for implementing both global css and css modules together as discussed here: https://github.com/css-modules/css-modules/pull/65 and here: https://github.com/kitze/custom-react-scripts/issues/29.

I'm using different loader tests for files which end with .css and files which end with .cssm.css indicating that they should be loaded using modules.

relevant part of config:

const extractTextPlugin = new ExtractTextPlugin({filename: '[name].[id].[contenthash].css', allChunks: true});

return {
    module: {
        rules: [
            {
                test: /\.cssm.(css|less)$/,
                loader: extractTextPlugin.extract({
                    fallbackLoader: 'style-loader',
                    loader: [
                        {
                            loader: 'css-loader',
                            query: {
                                importLoaders: 1,
                                modules: true,
                                localIdentName: '[name]_[local]_[hash:base64:5]'
                            }
                        },
                        {
                            loader: 'postcss-loader',
                            query: {
                                ident: 'postcss',
                                plugins: function() {
                                    return [
                                            require('autoprefixer')
                                    ];
                                }
                            }
                        },
                        {
                            loader: 'less-loader'
                        }
                    ]
                })
            },
            {
                test: /\.(css|less)$/,
                include: paths,
                loader: extractTextPlugin.extract({
                    fallbackLoader: 'style-loader',
                    loader: [
                        {
                            loader: 'css-loader',
                            query: {
                                importLoaders: 1
                            }
                        },
                        {
                            loader: 'postcss-loader',
                            query: {
                                ident: 'postcss',
                                plugins: function() {
                                    return [
                                            require('autoprefixer')
                                    ];
                                }
                            }
                        },
                        {
                            loader: 'less-loader'
                        }
                    ]
                })
            }
        ]
    },
    plugins: [
        extractTextPlugin
    ]
};

I have tried suggested solutions like using webpack 1 style of writing loaders, but that didn't help.

I'm using webpack version: 2.6.1 and extract-text-webpack-plugin: 2.1.2.

I also tried other versions, which didn't seem to help either.

my global css files work fine, only the imported .cssm.css files are being ignored when used with extract-text-webpack-plugin.

How do I fix the problem of css module files not being extracted properly with other global css?

Kkt answered 9/7, 2017 at 15:46 Comment(1)
If I could upvote you again, I would. Thank you for the follow up with your answer.Festoon
K
6

Apparently my setup was fine. The problem was that I didn't include all of my style (css/less) files in the "entry" webpack configuration. The configuration passed the build phase, but didn't process the new .cssm.less files I added in my attempt to use css modules together with regular global css.

Now everything works! For future reference I will include my updated configuration for using css modules with global css (for production and development). Apparently in the newer versions of webpack and extractTextPlugin, the exact syntax ("use" vs "loader", "options" vs "query" etc...) does not matter anymore and works both ways.

For production, I'm adding the prefix cssm on all of my css modules class names in the "localIdentName" property so that I can later use PurifyCSSPlugin and whitelist every class containing cssm:

exports.setupSeparateStyles = function(paths, cssModulesPaths) {
    const extractTextPlugin = new ExtractTextPlugin({
            filename: '[name].[contenthash].css', 
            allChunks: true
        });

    return {
        module: {
            rules: [
                {
                    test: /\.(css|less)$/,
                    include: paths,
                    exclude: /\.cssm\.(css|less)$/,
                    use: extractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: [
                            {
                                loader: 'css-loader',
                                options: {
                                    importLoaders: 1
                                }
                            },
                            'postcss-loader',
                            'less-loader'
                        ]
                    })
                },
                {
                    test: /\.(css|less)$/,
                    include: cssModulesPaths,
                    use: extractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: [
                            {
                                loader: 'css-loader',
                                options: {
                                    importLoaders: 1,
                                    modules: true,
                                    localIdentName: 'cssm-[name]_[local]_[hash:base64:5]',
                                }
                            },
                            'postcss-loader',
                            'less-loader'
                        ]
                    })
                }
            ]
        },
        plugins: [
            new webpack.LoaderOptionsPlugin({
                options: {
                    postcss: [
                        require('autoprefixer')
                    ]
                }
            }),
            extractTextPlugin
        ]
    };
};

for development it's a lot simpler:

exports.setupInlineStyles = function (paths, cssModulesPaths) {
    return {
        module: {
            rules: [
                {
                    test: /\.(css|less)$/,
                    include: paths,
                    exclude: /\.cssm\.(css|less)$/,
                    use: [
                        'style-loader',
                        {
                            loader: 'css-loader',
                            options: {
                                importLoaders: 1
                            }
                        },
                        'postcss-loader',
                        'less-loader'
                    ]
                },
                {
                    test: /\.(css|less)$/,
                    include: cssModulesPaths,
                    use: [
                        'style-loader',
                        {
                            loader: 'css-loader',
                            options: {
                                importLoaders: 1,
                                modules: true,
                                localIdentName: '[name]_[local]_[hash:base64:5]'
                            }
                        },
                        'postcss-loader',
                        'less-loader'
                    ]
                }
            ]
        },
        plugins: [
            new webpack.LoaderOptionsPlugin({
                options: {
                    postcss: [
                        require('autoprefixer')
                    ]
                }
            })
        ]
    };
};
Kkt answered 17/7, 2017 at 8:55 Comment(4)
Why anyone use localIdentName: '[name]_[local]_[hash:base64:5]', why no one didn't use hash number less than five, I mean localIdentName: '[name]_[local]_[hash:base64:3]', when I use less than five some of hashed classes has same name and this happening made all css styles bad.Paronomasia
@AmerllicA: When processing the "localIdentName" parameter, the function interpolateName is called from the webpack/loader-utils library. The interpolateName function parameters can be found here: [link]github.com/webpack/loader-utils.Kkt
@AmerllicA: To adress your question: a full hash number is generated according to the selected "digestType" (base64). Then if you specify a hash "length" (5 as recommended in the css-loader decomentation - link or 3 in your case), a sub-string of the original hash number is returned with the length specified. Because this number is cut down, if you only use 3 chars, there is a good chance that the generated hash will not be a unique hash code and thus cause css problems.Kkt
I create a new way to use RandomName, not hash, random name, it works awesome and I can make many different names. read this question and please vote it up, thanks. stackoverflow.com/questions/47580113Paronomasia

© 2022 - 2024 — McMap. All rights reserved.