mini-css-extract-plugin: Asset links broken when extracting CSS and assets to different folders

  • Operating System: OSX
  • Node Version: v14.15.4
  • NPM Version: 6.14.10
  • webpack Version: 5.15.0
  • mini-css-extract-plugin Version: ^1.3.5

Expected Behavior

I’m using MiniCssExtractPlugin to extract CSS, and with default settings everything works fine. However I want to export the CSS to its own folder, separate from image assets, like this:

build/styles/bundle.css
build/assets/image.png

Per the docs, I’m doing this by passing a filename option to the plugin with the desired folder name.

Actual Behavior

When I pass in an option like: filename: 'styles/[name].css, the CSS file itself gets exported to the correct directory. However, url(..) asset links inside the CSS aren’t adjusted, and wind up broken as a result.

Specifically, styles/bundle.css winds up containing references like url(assets/image.png), causing the browser to request styles/assets/image.png.

Code

The relevant code snippets look like this:

/* webpack.config.js */
    output: {
        filename: 'js/[name].[contenthash:8].js',
        path: path.resolve('.', 'build'),
        publicPath: '',
    },
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({ /* .. */ }),
        new MiniCssExtractPlugin({
            filename: 'styles/[name].css' }
        ),
    ],
    module: {
        rules: [
            // ...
            {
                test: /\.png$/,
                type: 'asset/resource',
                generator: { filename: 'assets/[name][ext]' },
            },
            {
                test: /\.css$/,
                use: [ MiniCssExtractPlugin.loader, 'css-loader' ],
            },
/* /src/foo/bar.css */
.whatever {
    background-image: url('~path/to/image.png');
}

After running webpack, the CSS winds up with broken asset links:

/* styles/bundle.css */
.whatever {
    background-image: url('assets/image.png');
        /* should be: url('../assets/image.png') or similar */
}

How Do We Reproduce?

By building a project with configuration as described above (in production mode).


Is there some setting or workaround supporting this use case?

Thanks!

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 46 (29 by maintainers)

Most upvoted comments

@alexander-akait Take your time, there’s no rush. I’m already working around the issue myself; I only posted this issue for others who might encounter it.

I’m trying in good faith to report an issue. There’s no call to be referring to it as trash or not a real problem.

Here is a minimal repro. Config, code, and steps to reproduce are same as posted previously.

For a closed issue?

It was closed, because you don’t provide enough information to help, if we will keep opened all issues where developers doesn’t provide enough formation, here will be trash and will be hard for developers found real problem(s), I will help and/or reopen it if you provide steps to reproduce

@andyhall OK, please upgrade to 1.3.10 of @cactuslab/mini-css-extract-plugin. This now looks at the output filename template to work out any additional relative paths to consider. In your case, that is styles/[name].css so we get an extra ../ which is what we need.

In order to make it work with your repo I removed the publicPath line from webpack.config.js (as "auto" is the default), upgraded the plugin, and updated the require statement in webpack.config.js, then ran npm run build.

I am really straying into an area of interacting with the webpack API that I’m not at all familiar with. I hope there’s a function to render a filename template given a chunk… I’ve just regexed the name into the template in case the name contains path components!

If somebody have solutions right now, feel free to send a PR

Fixed mine using Webpack 5 Resource assets instead of using file-loader.

{
    test: /\.(png|jpe?g|gif)$/i,
    type: 'asset/resource',
},
{
    test: /\.css$/i,
    use: [
      {
        loader: MiniCssExtractPlugin.loader,
      },
      'css-loader',
      'postcss-loader',
    ],
},

Anyway, can you add couple tests for your PR?

I’m trying in good faith to report an issue. There’s no call to be referring to it as trash or not a real problem.

I understand you, but try to understand me too, I got very many issues every day and when developer provide only part of code, it requires me to spend efforts on recreating configuration and code to reproduce the problem and most of times not problems (from my experience it is 99% of problems), so other developers won’t get help or fixes/features because I will spend most of the time trying to reproduce the problem

publicPath: '' and publicPath: 'auto' is not the same, publicPath: 'auto' should do relative assets, publicPath: '' is just empty publicPath

Yes, that’s literally absolutely correct, however I think the intention of the two is the same. I think webpack 5 just made it explicit. The default was empty; the default is now auto.

An empty publicPath is broken for CSS when the CSS is located out of the root. But people expect it to work as I think they want it to mean “use relative paths and make it work”, which I think is synonymous with auto.

That’s why I’m curious what breaks by adopting publicPath auto (or removing publicPath and thus using the default value of auto). My expectation is that everything just works, and where it doesn’t there lie more bugs to fix!

Yep, it is good fix, need couple test and we can merge it

@alexander-akait Perhaps a good first step is to take publicPath values "auto" and "" and “take over” and compute the relative URL ourselves, and see where that leads? I think that’s what I’ve done in the PR… I’ll try to improve the applying of the template this week.