Webpack-hot-middleware: Hot Module Replacement not reloading page After HTML changes

Created on 9 Aug 2017  路  8Comments  路  Source: webpack-contrib/webpack-hot-middleware

I'm having this issue using HtmlWebpackPlugin along side webpack-hot-middleware where the hot middle-ware don't refresh the page after html changes .
and here is a snippet of my

webback.config.js

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

module.exports = {
  context: path.resolve(__dirname), // string (absolute path!)

  entry: { // string | object | array
    main:['webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true', './src/js/main']
  },
  // Here the application starts executing
  // and webpack starts bundling
  output: {

    path: path.resolve(__dirname, 'dist'), // string

    filename: 'js/[name].js', // string
    // the filename template for entry chunks

    publicPath: '/', // string
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Titanium',
      template: path.resolve(__dirname, './src/index.html'),
      inject: true,
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ],
  // list of additional plugins
  resolve: {
    extensions: [' ', '.js', '.es6']
  },
}

Server.js

// import express
const express = require('express');
// create the express instance
const app = express();

(function() {

  // Step 1: Create & configure a webpack compiler
  const webpack = require('webpack');
  const webpackConf = require('./webpack.config.dev.js');
  const webpackCompiller = webpack(webpackConf);

  // Step 2: Attach the dev middleware to the compiler & the server
  app.use(require("webpack-dev-middleware")(
    webpackCompiller,
    {
      publicPath: webpackConf.output.publicPath,

      stats: {
        colors: true
      },
    })
  );
  // webpackDevMiddleware serves the files emitted from webpack
  // over an express server
  // documentation : https://github.com/webpack/webpack-dev-middleware


  // Step 3: Attach the hot middleware to the compiler & the server
  app.use(require("webpack-hot-middleware")(webpackCompiller));
  // Webpack hot reloading attached to express server
  // documentation : https://github.com/glenjamin/webpack-hot-middleware

})();

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

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

here is the output on the console and change event are emitted by the browser view is not changed at all
pwboazxesde7x0eeyrzmdg

Most helpful comment

Working Solution

So here is how i managed to solve this problem thanks to @glenjamin and @mcmire on #23
And here is the demo repository

FILE STRUCTURE
screenshot_2

this is the augmented hot reload client
./devScripts/Client.js

// client.js
(function() {
    'use strict';
    const webpackHotMiddlewareClient = require('webpack-hot-middleware/client?reload=true');

    webpackHotMiddlewareClient.subscribe(function(payload) {
        if (payload.action === 'reload' || payload.reload === true) {
            window.location.reload();
        }
    });

    module.exports = webpackHotMiddlewareClient;
}());

this is the file watcher it watches for html changes using the chokidar utility
./devScripts/hotReloader.js

(function() {
  'use strict';
  const path = require('path');
  const chokidar = require('chokidar');

  function activate(server) {
    /**
     * Here, we use Chokidar to force page reloading for some other file types
     * like html changes or php if you want
     */
    const watcher = chokidar.watch([
        path.resolve(__dirname, '../index.html'),// index.html is on the root folder
    ]);
    watcher.on('ready', function() {
        console.log('Initial scan complete. Ready for changes');
    });
    watcher.on('change', function(path) {
        console.log('File [' + path + '] changed !');
        // reload the client on file changes
        server.reloadClient();
    });
  }
  // here we export an activate function to activate the watcher
  module.exports = {
    activate: activate,
  };
}());

now we have to Setup the express server

./devScripts/server.js

const path = require('path');
// import express
const express = require('express');

// import webpack and the dev & hot middlewares
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');

function createServer() {
  // Step 1: create the express instance
  const app = express();

  // Step 2: Create & configure a webpack compiler
  const webpackConf = require('../webpack.config.dev.js');
  const webpackCompiller = webpack(webpackConf);

  const hotMiddleware = webpackHotMiddleware(webpackCompiller);
  const devMiddleWare = webpackDevMiddleware(
    webpackCompiller,
    {
      publicPath: webpackConf.output.publicPath,
   });

  // Step 3: Attach the dev middleware and hot middleware to the server
  app.use(devMiddleWare);
  app.use(hotMiddleware);

  function startServer() {
    app.listen(3000, function(err) {
      if (err) {
        console.error(err);
        return;
      }
      // log server running
      console.log('Listening at http://localhost:3000/');
    });
  }// end function start server

  /**
   *
   */
  function reloadClient() {
    hotMiddleware.publish({action: 'reload'});
  }// end function RelaodClient

  return {
    start: startServer,
    reloadClient: reloadClient,
  };
}
module.exports = createServer();

finally we have the root index.js which is the bootstrap file that runs all of this

./index.js

const path = require('path');
const server = require(path.resolve(__dirname,'./devScripts/server'));
const hotReloader = require(path.resolve(__dirname,'./devScripts/hotReloader'));

// Activate the costum hotReloader
hotReloader.activate(server);
// start the server
server.start();

now the webpack config contents are as follow
webpack.config.dev.js

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

module.exports = {
  context: path.resolve(__dirname),  // the home directory for webpack

  // here we import the augmented (custom) hot reloader client
  entry: { // string | object | array
    main:['./devScripts/client', './app']
  },

  output: {

    path: path.resolve(__dirname, 'dist'), // the target directory for all output files

    filename: 'js/[name].js', // the filename template for entry chunks

    publicPath: '/', // the url to the output directory resolved relative to the HTML page

  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new HtmlWebpackPlugin({
      title: 'Titanium',
      template: path.resolve(__dirname, './index.html'),
      inject: true,
    }),
  ],
  devtool: 'source-map', // use source-map for production
  stats: 'errors-only',  // lets you precisely control what bundle information gets displayed
}

All 8 comments

This is after you make an HTML change?

The HTML plugin doesn't interact the usual way with the bundle, so the standard hot reloading API doesn't work with it.

You might be able to use the custom-even binding stuff from https://github.com/glenjamin/webpack-hot-middleware/issues/23 to wire up some custom behaviour when the HTML changes.

Sorry this isn't documented, i'll make a note to get this added.

I vaguely remember seeing some examples of people doing this for html plugin, but can't remember where now.

Yes @glenjamin it's after i make an HTML change and save the files knowing that the cli console pickup the changes but nothing is updated on the browser view and as you said i saw a lot of threads about this specific problem but non of them was useful most of the issues discussions are related to react specific behaviour so non of them is useful here not sure even if it's a webpackHTMLPlugin 's problem or hot reload's related and i really need to find a way around or a hack

Working Solution

So here is how i managed to solve this problem thanks to @glenjamin and @mcmire on #23
And here is the demo repository

FILE STRUCTURE
screenshot_2

this is the augmented hot reload client
./devScripts/Client.js

// client.js
(function() {
    'use strict';
    const webpackHotMiddlewareClient = require('webpack-hot-middleware/client?reload=true');

    webpackHotMiddlewareClient.subscribe(function(payload) {
        if (payload.action === 'reload' || payload.reload === true) {
            window.location.reload();
        }
    });

    module.exports = webpackHotMiddlewareClient;
}());

this is the file watcher it watches for html changes using the chokidar utility
./devScripts/hotReloader.js

(function() {
  'use strict';
  const path = require('path');
  const chokidar = require('chokidar');

  function activate(server) {
    /**
     * Here, we use Chokidar to force page reloading for some other file types
     * like html changes or php if you want
     */
    const watcher = chokidar.watch([
        path.resolve(__dirname, '../index.html'),// index.html is on the root folder
    ]);
    watcher.on('ready', function() {
        console.log('Initial scan complete. Ready for changes');
    });
    watcher.on('change', function(path) {
        console.log('File [' + path + '] changed !');
        // reload the client on file changes
        server.reloadClient();
    });
  }
  // here we export an activate function to activate the watcher
  module.exports = {
    activate: activate,
  };
}());

now we have to Setup the express server

./devScripts/server.js

const path = require('path');
// import express
const express = require('express');

// import webpack and the dev & hot middlewares
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');

function createServer() {
  // Step 1: create the express instance
  const app = express();

  // Step 2: Create & configure a webpack compiler
  const webpackConf = require('../webpack.config.dev.js');
  const webpackCompiller = webpack(webpackConf);

  const hotMiddleware = webpackHotMiddleware(webpackCompiller);
  const devMiddleWare = webpackDevMiddleware(
    webpackCompiller,
    {
      publicPath: webpackConf.output.publicPath,
   });

  // Step 3: Attach the dev middleware and hot middleware to the server
  app.use(devMiddleWare);
  app.use(hotMiddleware);

  function startServer() {
    app.listen(3000, function(err) {
      if (err) {
        console.error(err);
        return;
      }
      // log server running
      console.log('Listening at http://localhost:3000/');
    });
  }// end function start server

  /**
   *
   */
  function reloadClient() {
    hotMiddleware.publish({action: 'reload'});
  }// end function RelaodClient

  return {
    start: startServer,
    reloadClient: reloadClient,
  };
}
module.exports = createServer();

finally we have the root index.js which is the bootstrap file that runs all of this

./index.js

const path = require('path');
const server = require(path.resolve(__dirname,'./devScripts/server'));
const hotReloader = require(path.resolve(__dirname,'./devScripts/hotReloader'));

// Activate the costum hotReloader
hotReloader.activate(server);
// start the server
server.start();

now the webpack config contents are as follow
webpack.config.dev.js

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

module.exports = {
  context: path.resolve(__dirname),  // the home directory for webpack

  // here we import the augmented (custom) hot reloader client
  entry: { // string | object | array
    main:['./devScripts/client', './app']
  },

  output: {

    path: path.resolve(__dirname, 'dist'), // the target directory for all output files

    filename: 'js/[name].js', // the filename template for entry chunks

    publicPath: '/', // the url to the output directory resolved relative to the HTML page

  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new HtmlWebpackPlugin({
      title: 'Titanium',
      template: path.resolve(__dirname, './index.html'),
      inject: true,
    }),
  ],
  devtool: 'source-map', // use source-map for production
  stats: 'errors-only',  // lets you precisely control what bundle information gets displayed
}

Hi, thanks for sharing this. Can you comment on the reason for this workaround?

this because of problems when trying to use webpack-html-plugin with hot reload as @glenjamin stated The HTML plugin doesn't interact the usual way with the bundle

Got it. I've been having problems with this as well, but I wasn't using webpack-html-plugin. Just express and webpack-dev-middleware and I couldn't get it to work.

that would solve that problem too

+

Was this page helpful?
0 / 5 - 0 ratings

Related issues

benjarwar picture benjarwar  路  10Comments

chopfitzroy picture chopfitzroy  路  4Comments

Javison666 picture Javison666  路  7Comments

Cristy94 picture Cristy94  路  3Comments

orpheus picture orpheus  路  9Comments