webpack-dev-server: Multicompiler with multiple output paths breaks WDS

Moved from https://github.com/webpack/webpack/issues/3091 - by @drphelps:

I’m submitting a feature request

Webpack version: 2.x

Please tell us about your environment: OSX 10.x

Current behavior: Currently, a Multicompiler instance with a different output.path specified for each config will boil the outputPath property down to a “common” path. I’m referring to this block of code https://github.com/webpack/webpack/blob/547295ca0e91dd5bb2c446e2df19c887c6aa053c/lib/MultiCompiler.js#L51-L63. This breaks WDS filename resolution.

Expected/desired behavior: WDS should serve resources from both paths or at least offer the ability to choose the first/preferred output.path when constructing the filename. I need to be able to output my files to separate (not necessarily nested) directories without breaking resolution.

  • What is the motivation / use case for changing the behavior? In my setup, I am trying to build two separate apps in parallel and I need to be able to share resources between the two, including async modules. Consider the following config:
module.exports = [
  {
    context: __dirname,
    entry: "./app.js",
    output: {
      filename: "app.js",
      path: path.resolve(__dirname, './app/dist'),
      publicPath: '/static/javascript/dist/'
    },
    ...
  },
  {
    context: __dirname,
    entry: "./widget.js",
    output: {
      filename: "widget.js",
      path: path.resolve(__dirname, './widget/dist'),
      publicPath: '/widget/dist/'
    },
    ...
  }
]

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 26
  • Comments: 20 (4 by maintainers)

Commits related to this issue

Most upvoted comments

Any update on this?

part of the code:

server.js

const telcoConfig = require('./config/webpack.dev')({ env: 'development', app: 'telco' });
const marketplaceConfig = require('./config/webpack.dev')({ env: 'development', app: 'marketplace' });

webpack.dev.js

const hotMiddlewareScript = 'webpack-hot-middleware/client?path=__webpack_hmr&timeout=20000&reload=true&dynamicPublicPath=true';

    return webpackMerge(commonConfig({ env: ENV, app: app }), {

        entry: {

            'polyfills': ['./src/' + app + '/polyfills.browser.ts', hotMiddlewareScript],
            'vendor': ['./src/' + app + '/vendor.browser.ts', hotMiddlewareScript],
            'main': ['./src/' + app + '/main.browser.ts', hotMiddlewareScript]

        },

        devtool: 'cheap-module-source-map',

        output: {

            path: helpers.root('dist/' + app),

            publicPath: '/' + app + '/',

            filename: '[name].bundle.js',

            sourceMapFilename: '[name].map',

            chunkFilename: '[id].chunk.js',

            library: 'ac_[name]',
            libraryTarget: 'var',
        },

        plugins: [
        ],

    })

My solution to a similar problem with webpack multi compiller & react & HMR

I have 2 sub projects: app & widget

App located at localhost:8080/* (except localhost:8080/widget) Widget located at localhost:8080/widget

webpack.config.js

module.exports = [{
  entry: {
    app: [
      'src/app/index.js',
      // path = output.publicPath + __webpack_hmr
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true',
    ],
  },
  output: {
    publicPath: '/',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/app/index.html',
    }),
    new webpack.HotModuleReplacementPlugin(),
  ],
  // Other module 1 options
},{
  entry: {
    widget: [
      'src/app/index.js',
      // path = output.publicPath + __webpack_hmr !see the difference between entries!
      'webpack-hot-middleware/client?path=/widget__webpack_hmr&timeout=20000&reload=true',
    ],
  },
  output: {
    publicPath: '/widget',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/widget/index.html',
    }),
    new webpack.HotModuleReplacementPlugin(),
  ],
  // Other module 2 options
}]

dev-server.js

const
  fs = require('fs'),
  http = require('http'),
  express = require('express'),
  webpack = require('webpack')

const app = express()

;(function() {
  const webpackConfig = require(process.env.WEBPACK_CONFIG || './webpack.config')

  webpackConfig.forEach((config, index) => {
    const compiler = webpack(config)

    app
      .use(require('webpack-dev-middleware')(compiler, {
        publicPath: config.output.publicPath,
      }))
      .use(require('webpack-hot-middleware')(compiler, {
        log: console.log,
        path: `${config.output.publicPath}__webpack_hmr`,
        heartbeat: 10 * 1000,
      }))
  })
})()

if (require.main === module) {
  const server = http.createServer(app)
  server.listen(process.env.PORT || 8080, () =>
    console.log('Listening on %j', server.address())
  )
}

And… add react-hot-loader in app & widget entrypoints

Works great 👍

If you want to have the same output.publicPath for multiple configs, or have them nested so one is the root and the other is a sub-path, then you need to make sure the configs have the exact same setting for output.path otherwise only the first config will have effect. Not sure why this is, I spent several hours trial and error to find this out so I just wanted to mention it for future reference.

UPDATE: Seems like this only works if you have the exact same publicPath. Nesting them, for example having one config with output.publicPath = "/" and one with output.publicPath = "/sub" does not work.

UPDATE2: Seems like the trick is to set output.path to exactly follow output.publicPath. For example the following will work for nested paths:

config1.output.path = path.resolve(__dirname, "./dist");
config2.output.path = path.resolve(__dirname, "./dist/sub");
config1.output.publicPath = "/";
config2.output.publicPath = "/sub";

Instead of constantly asking for updates, you can help. PR https://github.com/webpack/webpack-dev-middleware/pull/151 is a good starting point, but was never finished.

This would be very nice to have!

We are trying to fit webpack into an existing toolchain, requiring multiple configurations. The real killer would be to have a webpack-dev-server serving the assets from both the configs, at the paths specified by each.

This bug blocks me to use the WDS, but I found a solution from https://github.com/glenjamin/webpack-hot-middleware, the webpack-hot-middleware allows me to use multi compiler instances.

const firstConfig = require('./config/first');
const secondConfig = require('./config/second');

let express = require('express');
let middleware = require('webpack-dev-middleware');
let app = express();

// Dev Server
[firstConfig, secondConfig].forEach(function (config) {
    let compiler = webpack(config);
    app.use(middleware(compiler, {
        publicPath: config.output.publicPath
    }));

    // Enables HMR
    app.use(webpackHotMiddleware(compiler, {
        log: console.log, path: config.output.publicPath + '__webpack_hmr', heartbeat: 10 * 1000
    }));

});


let server = app.listen(18088);

@SpaceK33z I pushed a quick fix which does the trick for me. Let me know what you think.

#187 resolved the root issue that @SpaceK33z moved over from webpack. If there are errors being thrown please do report it as a bug separately and we can triage. If there’s only difficulty in getting it to work, please head to StackOverflow or the webpack gitter for more discussion and peer troubleshooting.

Hi, was there any progress on this? I see that webpack/webpack-dev-middleware#187 fixed this problem in the middleware part, so that’s not a problem anymore, and the code actually looks like it’s supposed to be able to run multi-compiler configurations – anyone knows what may cause this not to work (as it looks like it should)?