Search code examples
javascriptcsswebpackweb-componentsass-loader

Stop extract-css-chunks-webpack-plugin from combining all CSS


I am using Webpack 4 to build a simple website with an express backend. Now that I am implementing a custom element I am confronted with an issue regarding the shadow DOM.

The issue is as follows: all of my SCSS is being combined into one CSS output file. I need it to remain separate so that the custom element's style can be dynamically added to the shadow DOM, when the element is connected.

I am using extract-css-chunks-webpack-plugin (which I think the issue is with), css-loader, postcss-preset-env and sass-loader to handle all SCSS within the app/site.

All my searching thus far has simply lead me to the exact opposite of what I need (people trying to combine their SCSS).

I understand I could build the custom element separately and then just import it into the project after it has been built but that means managing two building environments and then having to version control across both -- seems like a lot of overhead.

The project's folder structure is as follows:

root
--src/
----assets/
------js/
--------main.js
------scss/
--------main.scss
------web-components/
--------contact-modal.js
--------scss/
----------modal.scss

My current webpack dev config is as follows:

    **omitted**

    module: {
        rules: [{
                test: /\.(scss)$/,
                use: [{
                        loader: ExtractCssChunksPlugin.loader,
                        options: {
                            hot: true,
                        }
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true,
                        }
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            indent: 'postcss',
                            plugins: () => postcssEnv(),
                            sourceMap: 'inline',
                        },

                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true
                        }
                    }
                ]
            },

    **omitted**

    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new HtmlWebpackPlugin({
            template: 'src/views/pages/index.ejs',
            filename: 'index.html',
        }),
        new ExtractCssChunksPlugin({
            filename: 'assets/css/[name].css',
            chunkFilename: 'assets/css/[id].css',
        })
    ]

If there is a better way of using custom elements which I have overlooked I'd appreciate any feedback.

Thank you in advance.

EDIT:

Figure it would be beneficial to state that I am including the modal.scss file with an import in the contact-modal.js file.


Solution

  • In order to solve my issue while still using SCSS (to allow me to use vendor prefixing from postcss-loader) I ended up prefixing my modal scss with a .modal flag so modal.scss became main.modal.scss.

    I then edited my webpack config to have two rules for scss files: One which only affected .scss files and one which affected .modal.scss.

    Then, in my modal I imported the scss with a normal import style from './main.modal.scss'; to then append it to the shadow DOM in a <style></style> element.

    Code is as follows:

    New SCSS rule

                    test: /(\.modal\.scss)$/,
                    use: [
                        'css-loader',
                        {
                            loader: 'postcss-loader',
                            options: {
                                plugins: () => [ postcssPresetEnv() ],
                                sourceMap: 'inline'
                            }
                        },
                        'sass-loader'
                    ]
    

    Modified old SCSS rule

                    test: /(?<!\.modal)\.scss$/,
                    use: [{
                            loader: ExtractCSSChunksPlugin.loader,
                            options: {
                                hot: true,
                            }
                        },
                        {
                            loader: 'css-loader',
                            options: {
                                sourceMap: true,
                                importLoaders: 3
                            }
                        },
                        {
                            loader: 'postcss-loader',
                            options: {
                                ident: 'postcss',
                                plugins: () => [
                                    postcssPresetEnv()
                                ],
                                sourceMap: 'inline'
                            }
                        },
                        {
                            loader: 'sass-loader',
                            options: {
                                sourceMap: true,
                            }
                        }
                    ]
    

    In my contact-modal.js file

    import CSS from './scss/main.modal.scss';
    
        // ... omitted ...
    
        connectedCallback() {
    
        // ... omitted ...
    
        const style = document.createElement('style');
        style.innerHTML = CSS.toString();
        this.shadow.appendChild(style);
    
        // ... omitted ...
    }