Search code examples
webpackmini-css-extract-plugin

webpack mini-css-extract-plugin => output multiple css-files on single entry


How can I setup webpack for my gutenberg blocks to extract multiple css files and bundle these based on the name of the stylesheets.

Zack Gordon used the Extract Text Plugin for this with webpack 3, and that worked like a charm. But with webpack 4 I had to switch to the mini-css-extract-plugin, in which I can't get this to work anymore.

See my current setup below, so you can see what I'm trying to do.

This is my project folder:

Plugin folder
|-- [src]
|   |-- [block1]
|   |   |-- block1.js
|   |   |-- style.scss
|   |   `-- editor.scss
|   |-- [block2]
|   |   |-- block2.js
|   |   |-- style.scss
|   |   `-- editor.scss
|   `-- index.js
`-- [build]
    |-- index.js
    |-- style.build.css
    `-- editor.build.css

In block1.js / block2.js:

import './style.scss'
import './editor.scss'

In index.js:

import './block1'
import './block2'

In webpack.config.js:

const defaultConfig = require("./node_modules/@wordpress/scripts/config/webpack.config");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    ...defaultConfig,
    optimization: {
        ...defaultConfig.optimization,
        splitChunks: {
            cacheGroups: {
                style: {
                    name: 'style',
                    test: /style\.s?css$/,
                    chunks: 'all',
                    enforce: true,
                },
                editor: {
                    name: 'editor',
                    test: /editor\.s?css$/,
                    chunks: 'all',
                    enforce: true,
                },
            },
        },
    },
    plugins: [
        ...defaultConfig.plugins,
        new MiniCssExtractPlugin({
            filename: 'blocks.[name].build.css'
            }),
    ],
    module: {
        ...defaultConfig.module,
        rules: [
            ...defaultConfig.module.rules,
            {
                test: /\.s?css$/,
                exclude: /node_modules/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader',
                    'sass-loader'
                ],
            },

        ]
    },
};

Expected output:

[build]
|-- blocks.editor.build.css
|-- index.js
|-- blocks.style.build.css

Current output:

[build]
|-- blocks.editor.build.css
|-- editor.js
|-- index.js
|-- blocks.style.build.css
|-- style.js
`-- (...and indentical map files)

The current setup spits out two extra js-files I don't need (style.js/editor.js), but the big problem is that it also causes the block not to load in Wordpress. It does load when I'm not using splitChunks, but then all css is bundled in a single file... and I need two.

Comparing: index.js without splitChunks:

/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = "./src/index.js");
/******/ })

index.js with splitChunks:

/******/    // add entry module to deferred list
/******/    deferredModules.push(["./src/index.js","editor","style"]);
/******/    // run deferred modules when ready
/******/    return checkDeferredModules();
/******/ })

Solution

  • See my comment, changed my setup and now it works: (OK, it's not a single entry anymore, but hey :-)

    Project folder:

    Plugin folder
    |-- [src]
    |   |-- [block1]
    |   |   |-- block1.js
    |   |   |-- style.scss
    |   |   `-- editor.scss
    |   |-- [block2]
    |   |   |-- block2.js
    |   |   |-- style.scss
    |   |   `-- editor.scss
    |   `-- index.js
    |   `-- css.js
    `-- [build]
        |-- index.js
        |-- style.build.css
        `-- editor.build.css
    

    In css.js:

    import './block1/style.scss'
    import './block1/editor.scss'
    import './block2/style.scss'
    import './block2/editor.scss'
    

    In webpack.config.js:

    const defaultConfig = require("./node_modules/@wordpress/scripts/config/webpack.config");
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    
    module.exports = {
        ...defaultConfig,
        entry: {
            index: path.resolve( process.cwd(), 'src', 'index.js' ),
            css: path.resolve( process.cwd(), 'src', 'css.js' ),
        },
        optimization: {
            ...defaultConfig.optimization,
            splitChunks: {
                cacheGroups: {
                    style: {
                        name: 'style',
                        test: /style\.s?css$/,
                        chunks: 'all',
                        enforce: true,
                    },
                    editor: {
                        name: 'editor',
                        test: /editor\.s?css$/,
                        chunks: 'all',
                        enforce: true,
                    },
                },
            },
        },
        plugins: [
            ...defaultConfig.plugins,
            new MiniCssExtractPlugin({
                filename: 'blocks.[name].build.css'
                }),
        ],
        module: {
            ...defaultConfig.module,
            rules: [
                ...defaultConfig.module.rules,
                {
                    test: /\.s?css$/,
                    exclude: /node_modules/,
                    use: [
                        MiniCssExtractPlugin.loader,
                        'css-loader',
                        'postcss-loader',
                        'sass-loader'
                    ],
                },
    
            ]
        },
    };
    

    Output build folder:

    [build]
    |-- blocks.editor.build.css
    |-- index.js
    |-- blocks.style.build.css
    |-- style.js (ignore)
    |-- editor.js (ignore)
    |-- css.js (ignore)
    `-- (...and indentical map files)