Mini-css-extract-plugin: How to override `webpack.output.publicPath`

Created on 19 Mar 2018  ·  58Comments  ·  Source: webpack-contrib/mini-css-extract-plugin


this is mini-css-extract-plugin config:

 plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].css",
            publicPath: "../../../"
        })
    ]

but extract-text-webpack-plugin works as expect:

                use: extractStyle.extract({
                    fallback: "style-loader",
                    use: [cssLoader],
                    publicPath: '../../../'
                })
good first issue help wanted 3 (important) 5 (confusing) docs

Most helpful comment

{
  test: /\.css$/,
  use: [{
    loader: MiniCssExtractPlugin.loader,
    options: {
      publicPath: '../'
    }
  }, {
    loader: 'css-loader'
  }]
}

looks like working for me

All 58 comments

@fpsqdb can you describe use case?

Yep, please provide a use case as this feature was one of the more 'problematic' ones of ETWP and we should very carefully consider all possible ways to solve this else wise (if possible) before adding options.publicPath back to this plugin

I have the same problem, about background-image,
use url-loader limitoptions, when less than.

when npm run buld, it's result like this:

body{
    background:url(images/bdc52c4172ae217d3d74698729d4e935.jpg) no-repeat;
}

however, want't to be:

body{
    background:url(../images/bdc52c4172ae217d3d74698729d4e935.jpg) no-repeat;
}

How to config this?


webpack.config.js:

   module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            },
            {
                test:/\.(png|jpg|gif)$/,
                use:[{
                    loader:'url-loader',
                    options:{
                        limit:50,  //it's important
                        outputPath:'images'
                    }
                }]
            }
        ]
    }

    ,plugins:[
        new MiniCssExtractPlugin({
            filename:'css/index.css' //it's important
        })
    ]

@itstrive, Same issue!
My old sass config for webpack 3x: https://codepen.io/7iomka/pen/pLrbQM?editors=0010
My sass config modified for webpack 4: https://codepen.io/7iomka/pen/EEvyRK?editors=0010
want't to be:
from DEV in sass file I write:
background: url('../../assets/img/catalog/dcp-l5500dn/dcp-l5500dn-sm.jpg');

In webpack v3 we written something like this:
use: ExtractTextPlugin.extract({ publicPath: '../', fallback: 'style-loader', use: ...

In webpack 4 we don't have to pass publicPath: '../', in config, and
Expected result:
background: url(img/dcp-l5500dn-sm.jpg);
But real result is:
background: url(/img/dcp-l5500dn-sm.jpg);

Any help?
Thanks!

+1 on this, i have the same issue

Use publicPath option for loader, look here https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/src/loader.js#L33. Feel free to ping me if it is not help

@evilebottnawi same issue here. I'm using it like so:

plugins: [
  ...
  new MiniCssExtractPlugin({
    filename: '[name]-[contenthash].css',
    publicPath: './',
  }),
]

But I'm not seeing publicPath being attached to the query object. I.e.

const query = loaderUtils.getOptions(this) || {};

..is logging as an empty object. When I log the loader itself (MiniCssExtractPlugin.loader), it's just a relative path to the loader.js file, so I'm unclear on how to add the option there.

Thanks in advance!

@troygibb looks like feature, PR welcome :+1:

@evilebottnawi nice!! I'll try to get around to it in the next few weeks.

{
  test: /\.css$/,
  use: [{
    loader: MiniCssExtractPlugin.loader,
    options: {
      publicPath: '../'
    }
  }, {
    loader: 'css-loader'
  }]
}

looks like working for me

To make it work:

  • use a publicPath in devserver
  • use a relative path on the plugin entry (not loader)
  • use a relative path in filename in output

@wadouk I think you don't understand an issue.

If you use plugin like

new MiniCssExtractPlugin({
  filename:'css/index.css' //it's important
})

then all resources inside CSS file (like background images, fonts) become with wrong urls, they need to be prepended with "../", because your css file inside folder css/.

This is not publicPath on devserver, which completely different thing, and publicPath on plugin entry is not working, as reported by @troygibb . And you don't specify css files and resources as output entry, so you are wrong here too.

Setting publicPath as @victordidenko mentioned worked for me as well

+10086 on this, IIIIIIII have the same issue,too!

Somebody can create minimum reproducible test repo?

@evilebottnawi publicPath option for loader works as expected, as you said. See my comment above.

Hm what is problem, your want rewrite all assets in css using publicPath? It is impossible you should configure publishPath options for loader

That is exactly what my comment about. Do you follow conversation?

@victordidenko oh sorry, i look you post on email (very hard to read :smile: ). Yep close this issue because it should be set using loader. Thanks!

Also rewrite absolute URLs to relative inside your css should be done by css minificator.

I think it should be documented though. In readme there is nothing about possible options for loader.

@victordidenko yep, you are right

Is there a better alternative than overriding the publicPath which feels kinda hacky?
There must be a solution between not creating a /styles folder in production (thus dumping everything in root folder) and overriding the public path.

@victordidenko ,your comment couldn't work for me. I use scss in my project.

here is my configuration about that .

                {
                    test: /\.scss$/,
                    use:[
                        {
                            loader: MiniCssExtractPlugin.loader,
                            options: {
                                publicPath: '/static'
                            }
                        },{
                            loader: 'css-loader'
                        },{
                            loader: 'sass-loader'
                        }]
                }

some code in my scss like this

background-image: url("/mobile/img/logo-new.png");

I want to require the file "root/static/mobile/img/logo-new.png"

But exactly I got the file "path root/mobile/img/logo-new.png " and not got error info .

@zhangolve I guess you've met complex of different issues.

  1. According to https://github.com/webpack-contrib/sass-loader#problems-with-url url(...) should be relative, while you have absolute.
  2. You didn't tell, where is your main css file located, maybe you don't even need to rewrite publicPath for it, if it is located inside /static folder.
  3. Folder /static gives me an idea, that you probably use Next.js, and want to add scss for it. If so – read documentation for framework, about adding scss to the project.

thank you for the quick response and sorry for not fully telling my condition.
first my main css file is not located inside /static folder and secondly I am not use next.js.
maybe I should learn something about sass-loader.

@zhangolve did you add your scss files to noParse section? I think if webpack will parse pathes inside url(...) and you will set outputPath: 'static/' in file-loader for your images, webpack will set correct pathes in css, no need to change publicPath in loader.

  1. I did't add my scss files to noParse section.
  2. In my option,my question is webpack couldn't resolve my pictures according path inside url(...).And as you can see in my first comment about this issue.

background-image: url("/mobile/img/logo-new.png");

I want to require the file "root/static/mobile/img/logo-new.png"

But exactly I got the file path "root/mobile/img/logo-new.png " ,webpack can not resolve it .(can not find it according to the path) ,so file-loader (I use url-loader to handle images) couldn't handle the image.

  1. when I used webpack 3 + extract-text-webpack-plugin + sass-loader ,things more easy.

    add ExtractTextPlugin to plugins

        new ExtractTextPlugin({
            publicPath: '/static',
            filename: '[name].[contenthash].css',
            disable: false,
            allChunks: true
        })

and rule to test scss:

{
      test: /\.scss$/,
      use: ExtractTextPlugin.extract({
          use: [{
          loader: 'css-loader',
          options: {
           root: 'static',
            minimize: true
             }
            }, 'sass-loader'],
          fallback: 'style-loader'
          }),
         include: path.resolve(APP_SRC_PATH, 'scss')
       }

so when my scss url(...) like below:

background-image: url("/mobile/img/logo-new.png");

that will parse into background-image: url("/static/mobile/img/logo-new.png");

After compare with my webpack 3 config ( webpack 3 + extract-text-webpack-plugin + sass-loader) ,I find I need to add root option to css-loader ,and that for me works.

here is my test rule.

            {
                test: /\.scss$/,
                use:[
                    {
                        loader: MiniCssExtractPlugin.loader
                    },{
                        loader: 'css-loader',
                         options: {
                             root: 'static',
                             minimize: true
                         }
                    },{
                        loader: 'sass-loader'
                    }]
            }

any update?
I test it can't override the output.publicPath still. I need to set css file to different CDN. 😭

@victordidenko don't work for me 😢

@hxlniada does extract-text-webpack-plugin works as expected in your case? What are you trying to achieve? Can you add some code examples?

@hxlniada Use publicPath for loader option, also please read answer before post new. Thanks!

@evilebottnawi
yes i have read your answer, these are my config:

  // webpack.prod.js
    mode: 'production',
    output: {
        filename: 'js/[name].[chunkhash:7].js',
        publicPath: 'https://s1-8.bdimg.com/'
    },
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        publicPath: 'https://s2-8.bdimg.com/'
                    }
                },
                {loader: 'css-loader', options: {importLoaders: 1, minimize: true}},
                'postcss-loader',
                'sass-loader'
            ]
        }
     ...
    // index.ejs
   ...
   <% htmlWebpackPlugin.files.css.forEach(function (cssFile) { %>
        <link rel="stylesheet" href="<%= cssFile %>">
    <% }) %>
  ...

and the result:

<link rel=stylesheet href=https://s1-8.bdimg.com/css/2.5911c9a.css>

but the expected result is "https://s2-8.bdimg.com"
I have tried many many times.

@victordidenko haven't try it yet. is it not the recommended way to use extract-text-webpack-plugin with [email protected] ?

@hxlniada for loader set publicPath

@hxlniada no, this plugin is not working with webpack4, as far as I know, mini-css-extract-plugin is it's descendant. But I found this issue because extract-text-webpack-plugin was working this way with webpack3.
This option, publicPath, aimed to change _urls to resources inside CSS_. It has nothing to do with CSS files' URLs, as far as I understand.

@evilebottnawi how to implement this with MiniCssExtractPlugin? thank you.

@hxlniada

{
  test: /\.css$/,
  use: [{
    loader: MiniCssExtractPlugin.loader,
    options: {
      publicPath: '../'
    }
  }, {
    loader: 'css-loader'
  }]
}

Wait feedback 24 hours and lock issue to avoid QA/not related problems.
Thanks!

@evilebottnawi neither publicPath nor path work for me and doesn't seem to have any impact on the output path (maybe I've misunderstood this issue here and are facing another one?)

I wanna extract my imported SCSS files and save them to /assets/dist/css/.
My assets are located at

/assets
  /dist
    /css
    /js
  /src
    /js
      app.js
    /scss
      example.scss

My app.js has a dynamic import

import(/* webpackChunkName: 'example' */ '../scss/example.scss');

My webpack config looks like this

{
    test: /\.scss$/,
    use: [
        {
            loader: MiniCssExtractPlugin.loader,
            options: {
                path: '../css',
            }
        },
        {
            loader: 'css-loader'
        },
        {
            loader: 'postcss-loader'
        },
        {
            loader: 'sass-loader'
        }
    ]
}

But it doesn't matter if I use path/publicPath option or not – the output path is always /assets/dist/js/example.css.
The only way I've found was adjusting the filename in the plugin instance

new MiniCssExtractPlugin({
    filename: '../css/[name].css'
})

But this feels a bit hacky or is it the recommended way in my case?
Would prefer to have some option for this.

@SassNinja provide full config using gist, I recommend not touching publicPath never, it is does make sense not for production not for development (if you don't use webpack for library).

https://webpack.js.org/guides/public-path/

The publicPath configuration option can be quite useful in a variety of scenarios. It allows you to specify the base path for all the assets within your application.

Let's say, you have structure:

.
├── app.js
├── fonts
│   └── font.woff
├── images
│   └── logo.png
└── main.css

Inside your css you have links to font and logo:

src: url(fonts/font.woff);
src: url(images/logo.png);

When you build application using webpack, it see all dependencies, and put right new names-links for them, relatively to publicPath.

But if you want to move your webpack-generated CSS file to folder css/ instead of root (where it is located), you use smth like this:

new MiniCssExtractPlugin({
  filename: 'css/[name].css'
})

And here is where issue lays: Webpack doesn't replace links inside generated CSS according to new CSS file position in file system, all links stays unchanged, relative to global publicPath:

src: url(fonts/123456somehash.woff);
src: url(images/876453somehash.png);

But your CSS located inside css/ folder, so all links inside it become invalid! So you want override publicPath for assets inside CSS to

src: url(../fonts/123456somehash.woff);
src: url(../images/876453somehash.png);

And you use publicPath option for loader for that.

Again, it doesn't change _location for your CSS files_, this is _not output path_!
It overrides publicPath for assets _inside CSS_!

@SassNinja using filename is exactly right way in your case

_Guys, this is not a QA_

@victordidenko thanks for good clarify

Little note:

And you use publicPath option for loader for that.

It is file-loader or url-loader (depends on the configuration) in 99% configuration

I can't seem to get this to rewrite the urls correctly in webpack 4 anymore. in my config I have:

------------
new MiniCssExtractPlugin({
  filename: "styles/[name].css"
})
...
Module Rules
----------------
{
      test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|otf|ico|mp4)(\?\S*)?$/,
      use: {
        loader: 'file-loader',
        options: {
          outputPath: 'assets/',
          publicPath: '/',
          name: '[name].[ext]'
        }
      },
    }
...
{
      test: /\.(css|scss)$/,
      exclude: [helpers.projectRoot('node_modules')],
      include: [helpers.projectRoot('src/styles')],
      use: [{
        loader: MiniCssExtractPlugin.loader,
        options: {
          publicPath: '../assets'
        }
      }, {
        loader: 'css-loader'
      }, {
        loader: 'sass-loader'
      }]
    }

I want the urls in my scss to be changed to url(/assets/barlow-v1-latin-regular.woff) but everything is unchanged and from website root url('./web-theme/fonts/Barlow/barlow-v1-latin-regular.woff') is turning into http://lvh.me:3000/web-theme/fonts/Barlow/barlow-v1-latin-regular.woff

The actual assets are at the correct location, they just aren't loading because the url has the wrong path.

Any help would be greatly appreciated. I have tried several permutations of the suggested publicPath option for the loader but nothing seems to have any effect.

For anyone who is having the same problem I was with a configuration similar to mine. Before, with Webpack 3, ExtractTextPlugin seemed to be updating the path for my assets. After, with Webpack 4, MiniCssExtractPlugin was not. The solution was to change the "publicPath" of the "file-loader" to point to my assets folder. I actually removed "publicPath" from the MiniCssExtractPlugin "options". Hope this can help someone else.

@ClaytonHunt
你的意思是,file-loader -> publicPath,MiniCssExtractPlugin -> publicPath 这两者参数多要设置?还是只需要设置file-loader 上设定publicPath的参数?
我目前只是修改了MiniCssExtractPlugin -> publicPath 这个参数,背景图引入路径已经被纠正!

You mean, file-loader.publicPath, MiniCssExtractPlugin.publicPath, both of which need to be set up. Or do you just need to set the publicPath parameter on the file-loader?
I currently only modify the MiniCssExtractPlugin.publicPath parameter, the background image introduction path has been corrected!

image

Please don't spam here, use publichPath option for mini-css-extract-plugin loader, if it is not help double check you publichPath on other loader, thanks!

@xiaofan9 I only needed publicPath on file-loader.

@evilebottnawi I apologize if you think we are spamming, the fact is that the suggested fixes in this thread don't seem to fix the problem. I was originally presenting my config and asking for help. Once I got something working I thought it would be helpful to share what worked for me. I dealt with this issue for more than a day before I posted asking for help and then figured it out after 5 more days of tinkering. I wanted to save others the time I wasted.

@ClaytonHunt not for you, just notice about posts like "I have some problem" (without configuration or minimum reproducible test repo)

@evilebottnawi I am sorry, I repeat the editor is afraid I am not fully expressed, I will pay attention next time.

@ClaytonHunt Using only publicPath on file-loader, I don't know if you will encounter the problem that the src in img can't find the path?

@evilebottnawi Hi i am using react-loadable for lazy loading js files and MiniCssExtractPlugin for lazyloading css files. now i want to load js and css files from different domains. how can achieve this problem?

+1 on this, i have the same issue

+2 on this, i have the same issue

exports.cssLoaders = function(options) {
options = options || {}

const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}

const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}

const MiniCssExtractPluginLoader = {
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it use publicPath in webpackOptions.output
publicPath: '../../'
}
}

// generate loader string to be used with extract text plugin
function generateLoaders(loader, loaderOptions) {
const loaders = []

// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
  loaders.push(MiniCssExtractPluginLoader)
} else {
  loaders.push('vue-style-loader')
}

loaders.push(cssLoader)

if (options.usePostCSS) {
  loaders.push(postcssLoader)
}

if (loader) {
  loaders.push({
    loader: loader + '-loader',
    options: Object.assign({}, loaderOptions, {
      sourceMap: options.sourceMap
    })
  })
}
return loaders

}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', {
indentedSyntax: true
}),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}

Using publicPath on loader didn't do anything for me, what solved it was to use context on the css-loader directly.

use: [
    cssHotLoad,
    MiniCssExtractPlugin.loader,
    {
        loader: 'css-loader',
        options: {
            importLoaders: 2,
            sourceMap: process.env.NODE_ENV === 'development',
            url: false,
            context: '../../',
        }
    },
]

I ended up with this configuration to get the output files to go where needed.

webpack.common.js:

    output: {
        path: helpers.root('public'),
        publicPath: '',
    },

webpack.prod.js:

    output: {
        filename: 'js/[hash].js',
        chunkFilename: 'js/[id].[hash].chunk.js'
    },
...
    plugins: [
        new MiniCSSExtractPlugin({
            filename: 'css/app.[hash].css'
        })
    ]

hopefully @evilebottnawi

Was this page helpful?
0 / 5 - 0 ratings