mini-css-extract-plugin: `loader.options.publicPath` not being honored

Hi! If I haven’t been mistaken, should the publicPath -option allow me to output generated bundles to the given destination?

My baseconfig has this as publicPath:

output: {
  publicPath: "/scripts/",
  path:       SCRIPT_DIR  // this points to "/public/scripts"
}

Then on my derived production config has:

if (TARGET_ENV === "production") 
  commonConfig = merge(commonConfig, {
    entry: {
      app: `${APP_DIR}/index.tsx`
    },
    output: {
	filename:      "[name].[chunkhash].js",
	chunkFilename: "[name].[chunkhash].js"
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            {
              loader: MiniCSSExtractPlugin.loader,
              options: {
                publicPath: "/styles/",
	      }
            },
            "css-loader?-url",
	    "postcss-loader"
        ]
    }
    plugins: [
      new WebpackChunkHashPlugin(),	// Hash from content (to output options)
      new HtmlWebpackPlugin({
        filename: "../index.html",
        title:    PROJECT_TITLE,
	template: `${APP_DIR}/index.ejs` 
      }),
      new InlineChunkManifestHtmlWebpackPlugin(),
      new MiniCSSExtractPlugin({ 
        filename:      "[name].[hash].css",
        chunkFileName: "[id].[hash].css" 
      })
    ]
});

I’m attempting to get a structure where JS goes to public/scripts/ and CSS public/styles. JS ends up in the right place. The CSS doesn’t seem to.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 5
  • Comments: 19 (9 by maintainers)

Most upvoted comments

I am also experiencing this. I try to break it down below, with my current solution.

Webpack output object

output: {
    path: path.resolve(__dirname, "./App/JavaScript"),
    publicPath: "/App/JavaScript/"
}

Loader object

{
  loader: MiniCssExtractPlugin.loader,
  options: {
    publicPath: "../bananas/"// As an example
  }
}

Finally, the plugin config

new MiniCssExtractPlugin({
  filename: "Styles.css"
})

This will generate the Styles.css at /App/JavaScript/. The expected folder “bananas” never gets created in /App/. If a relative path is put into the filename, it will generate the file in the correct location /App/bananas/Styles.css

new MiniCssExtractPlugin({
  filename: "../bananas/Styles.css"
})

For this filename you probably need publicPath: "../" in the MCEP loader.

@michael-ciniawsky I’m having the problem too, and I have a barebones setup with nothing mini-css-extract-plugin. File output is going to webpack.output which is typically JS related.

  module : {
    rules: [{
      test: /\.scss$/,
      use: [
        {
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: "../css/"
          }
        },
        "css-loader", // translates CSS into CommonJS
        "sass-loader" // compiles Sass to CSS, using Node Sass by default
      ]
    }]
  },

kk what is inlining the paths atm, the html-webpack-plugin ? The loader.options.publicPath might not be honored by the html-webpack-plugin or it gets overridden somewhere else, either case is definitely a bug

Yes, I have also seen the relative path looking a little ugly. My real world project looks like this:

<link href="/App/JavaScript/../StyleSheets/CSS/Styles.css" rel="stylesheet">

Again, I agree it is not a big issue and it does work this way. I did look at the source code a bit yesterday in src/loader.js, line 37.

const publicPath =
    typeof query.publicPath === 'string'
      ? query.publicPath
: this._compilation.outputOptions.publicPath;
const outputOptions = {
    filename: childFilename,
    publicPath,
};

My only thought looking at it was that publicPath in outputOptions was not set as a property. I will amend my local NPM copy and see if it makes a difference.