Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get a multi-app webpack configuration to work with react-hot-loader

I have a basic setup with two apps each in a separate directory, I'm using a custom server to compile them using webpack-dev-middleware/webpack-hot-middleware. Things are working fine except that I can't get HMR to work for the second app (I'm using react-hot-loader).

Here's a minimal repo illustrating the problem: https://github.com/AmrN/multi-react-app-hmr

My main code files:

webpack.config.js

var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = function (appName) {
  return {
    devtool: 'cheap-module-eval-source-map',
    entry: [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client',
      path.join(__dirname, appName, 'index'),
    ],
    output: {
      path: path.join(__dirname, 'dist', appName),
      filename: 'bundle.js',
      publicPath: '/'+appName+'/'
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NamedModulesPlugin(),
      new webpack.NoEmitOnErrorsPlugin(),
      new HtmlWebpackPlugin({
        template: path.join(__dirname, appName, 'index.html'),
      }),
    ],
    module: {
      loaders: [{
        test: /\.jsx?$/,
        loaders: ['babel-loader'],
        exclude: /node_modules/,
      }]
    },
  };
};

server.js

var path = require('path');
var webpack = require('webpack');
var express = require('express');
var config1 = require('./webpack.config')('app1');
var config2 = require('./webpack.config')('app2');

var app = express();

[config1, config2].forEach((config) => {
  var compiler = webpack(config);
  app.use(require('webpack-dev-middleware')(compiler, {
    publicPath: config.output.publicPath
  }));

  app.use(require('webpack-hot-middleware')(compiler));
});

app.listen(3000, function (err) {
  if (err) {
    return console.error(err);
  }

  console.log('Listening at http://localhost:3000/');
});

(app1|app2)/index.js

import { AppContainer } from 'react-hot-loader';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const rootEl = document.getElementById('root');
const render = Component =>
  ReactDOM.render(
    <AppContainer>
      <Component />
    </AppContainer>,
    rootEl
  );

render(App);
if (module.hot) module.hot.accept('./App', () => render(App));

Now if I run the server, my files are compiled correctly, and I can visit http://localhost:3000/app1/index.html successfully, and HMR is working properly here. However, if I visit the second app http://localhost:3000/app2/index.html it opens but HMR is not working and looking at the console it gives me the following error:

GET http://localhost:3000/app2/640a44b6b47b67436af2.hot-update.json 404 (Not Found)

[HMR] Cannot find update (Full reload needed)

[HMR] (Probably because of restarting the server)

Another thing I've noticed is that changing the order in which I apply my apps webpack configs in server.js from:

[config1, config2].forEach((config) => {...})

to:

[config2, config1].forEach((config) => {...})

switches the problem to app1, now HMR works for app2 but not app1.

Any help is appreciated, thanks.

like image 207
Amr Noman Avatar asked Jun 11 '17 23:06

Amr Noman


People also ask

How do I enable hot module replacement in Webpack?

To enabling HMR in your project, you need to let your application know how to handle hot updates. You can do so by implementing the module. hot API exposed by Webpack. Once the hot update is accepted, the HMR runtime and the loaders will take over to handle the update.

How does Webpack hot reload work?

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways: Retain application state which is lost during a full reload. Save valuable development time by only updating what's changed.


1 Answers

The problem was that both apps used the same path for hot reloading (I think it's /__webpack_hmr by default). So I had to use a different one for each:

in webpack.config.js I did:

entry: [
  // ...
  'webpack-hot-middleware/client?path=/__webpack_hmr_'+appName,
  // ...
]

and in server.js:

app.use(require('webpack-hot-middleware')(compiler, {
  path: '/__webpack_hmr_'+appName
}));

Now it's working properly.

like image 61
Amr Noman Avatar answered Nov 15 '22 07:11

Amr Noman