webpackwebpack-5css-loader

Webpack 5 - Error: Module parse failed: Unexpected character '@'


I am attempting to load some .scss files in webpack 5 and then output them as .css files. I have setup my config to take only .scss files as entries and use MiniCssExtractPlugin, css-loader, resolve-url-loader and sass-loader.

Here is my conifg:

{
    target: 'web',
    stats: {
        errorDetails: true
    },
    entry: ScssEntryGenerator.generateStaticEntryPoint(),
    output: {
        filename: '[name].css',
        path: path.resolve(__dirname)
    },
    module: {
        rules: [
            {
                test: /\.scss$/i,
                use: [
                    'css-loader',
                    'resolve-url-loader',
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true, // <-- !!IMPORTANT!!
                        }
                    },
                    MiniCssExtractPlugin.loader,
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css'
        })
    ],
};

There is an output of js which contains references to .scss files:

var __webpack_exports__ = {};
__webpack_modules__["./Develop/Styles/accessible/site.scss"]();

And there is an error thrown by webpack, as if the sass-loader wasn't used at all:

Error: Module parse failed: Unexpected character '@' (4:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

P.S. If I move MiniCssExtractPlugin.loader to the top of the use array, as in the docs webpack breaks due to files with the same names being outputted and conflicting.


Solution

  • From my research, it is necessary to have the mini-css-extract-plugin loader as the first loader in the array, which means I've been going in the wrong direction.

    Thanks to a webpack plugin, LimitChunkCountPlugin, I have limited the amount of chunks generated to 1 and now i end up only with the css and js for every entry point I have, which is close enough to what I wanted, without any conflicts such as:

    Conflict: Multiple assets emit to the same filename

    What is also necessary is to pay attention to the filename in the output and the filename in the MiniCssExtractPlugin plugin config, they can't be the same.

    For me, they are [name].js and [name].css respectively.

    The reason for using an empty function in the output filename prevents webpack from generating additional js files for each entry.

    This is my config now:

    const rootStaticStyleConfig = {
        target: 'web',
        stats: {
            errorDetails: true
        },
        devtool: 'source-map',
        entry: ScssEntryGenerator.generateStaticEntryPoint(),
        output: {
            filename: '[name].js',
            path: path.resolve(__dirname),
            iife: false
        },
        module: {
            rules: [
                {
                    test: /\.scss$/i,
                    use: [
                        {
                            loader: MiniCssExtractPlugin.loader
                        },
                        {
                            loader: 'css-loader',
                            options: {
                                sourceMap: true
                            }
                        },
                        {
                            loader: 'sass-loader',
                            options: {
                                sourceMap: true,
                                sassOptions: {
                                    includePaths: [path.resolve(__dirname, 'Develop', 'Styles')]
                                }
                            }
                        }
                    ]
                }
            ]
        },
        plugins: [
            new MiniCssExtractPlugin({
                filename: '[name].css',
                runtime: false,
            }),
            new webpack.optimize.LimitChunkCountPlugin({
                maxChunks: 1
            })
        ],
    };