Webpack-dev-server: Documentation/HMR instructions missing regarding addDevServerEntrypoints

Created on 23 Aug 2017  路  10Comments  路  Source: webpack/webpack-dev-server

I tried to follow the instructions to enable HMR using the node API. My code looked like this

let config = require('./config.js'); //it includes the HMR plugin
const compiler = webpack(config);
const server = new webpackDevServer(compiler, {
  hot: true,
  inline: true,
  stats: {
    colors: true
  },
  historyApiFallback: true,
  port: port,
  contentBase: path.join(process.cwd(), './devdist/'),
  host: '0.0.0.0',
  disableHostCheck: true
});

server.listen(port, () => {
  console.log('dev server listening on port ', port);
});

and reloading on changes didn't work. I noticed it's because the connection event for the sockjs server never fired (sockServer.on("connection", (conn) => {...}))

this happened when i switched from the CLI to the node API, and the difference seemed to be that I wasn't calling addDevServerEntrypoints, while the CLI did. I tried calling it before creating the compiler object and things started working.

Is this the expected way to enable HMR with the node API? If so, should we document it? I see this comment as to why addDevServerEntryPoints is exported
// Export this logic, so that other implementations, like task-runners can use it and it seems like it's the solution to the problem, so it should be in the docs. Either that or it should be called for you if hot is true or something. Not totally sure what the right thing to do there is.

here's my new script, which works

let config = require('./config.js'); //it includes the HMR plugin
let serverOptions = {
  hot: true,
  inline: true,
  stats: {
    colors: true
  },
  historyApiFallback: true,
  port: port,
  contentBase: path.join(process.cwd(), './devdist/'),
  host: '0.0.0.0',
  disableHostCheck: true
};

webpackDevServer.addDevServerEntrypoints(config, serverOptions);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, serverOptions);

server.listen(port, () => {
  console.log('dev server listening on port ', port);
});

Most helpful comment

Thanks @bdwain. I'm sure that'll help out some folks. Gonna close this one here for the time being. Ping me if you need it to be reopened!

All 10 comments

(please do follow the issue template moving forward, even if it doesn't quite fit the issue, so we're all setting a good example for non-contributors, non-collaborators)

I think in the near term, we should definitely document that. It's quite an omission, and that's a great catch 馃憤 . For the long term, we should look at any implications into automatically calling that for hot: true implementations. eg. can we sneak that into a patch release without breaking things for folks already calling that separately.

sorry i wasnt really sure if it was a bug or a doc omission so i wasnt sure how i should fill out the template. ill use it next time.

i can do a PR to update the docs. it's just https://github.com/webpack/webpack.js.org right?

correct!

Thanks @bdwain. I'm sure that'll help out some folks. Gonna close this one here for the time being. Ping me if you need it to be reopened!

I tried this method according to the documentation and got error:

WebpackDevServer.addDevServerEntrypoints(config, options)
                 ^
TypeError: WebpackDevServer.addDevServerEntrypoints is not a function
    at Object.<anonymous> (/opt/webpack/server.dev.js:28:18)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:383:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:496:3

Is the documentation still relevant?

@bdwain: That saved me. Thank you. 馃憤

I don't claim to understand why, but for me the addDevServerEntrypoints call was not enough.

I also had to modify config.entry as described here, in particular:

config.entry.app.unshift(
  `webpack-dev-server/client?http://localhost:${port}/`,
  `webpack/hot/dev-server`
);

Setting up Hot Module Replacement with webpack-dev-server via the Node.js API is pretty tricky.

When using the CLI, all I had to do was adding new webpack.HotModuleReplacementPlugin() in the plugins section of my webpack.config.js and it worked by simply executing webpack-dev-server. As an alternative, I could run webpack-dev-server --hot which then automatically adds the HotModuleReplacementPlugin.

When using the Node.js API, you will have to do three steps:

  1. Set devServer.hot to true
  2. Add new webpack.HotModuleReplacementPlugin() to plugins
  3. Modify your app's entry point as described by @indirectlylit

The WebpackDevServer.addDevServerEntrypoints function got removed, so it's no longer of use (tested with webpack 4.26.1 & webpack-dev-server 3.1.10).

Here is a setup with HMR that I use in my Gulp task:

gulpfile.js

const chalk = require('chalk');
const gulp = require('gulp');
const gutil = require('gulp-util');
const webpack = require('webpack');
const webpackConfig = require('./webpack.config');
const WebpackDevServer = require('webpack-dev-server');

gulp.task('watch:frontend', () => {
  const config = {...webpackConfig};
  config.mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';

  const host = config.devServer.host;
  const port = config.devServer.port;
  const frontendUrl = `http://${host}:${port}`;

  config.entry[process.env.npm_package_name].unshift(`webpack-dev-server/client?${frontendUrl}/`, 'webpack/hot/dev-server');

  const compiler = webpack(config);
  const server = new WebpackDevServer(compiler, config.devServer);

  server.listen(port, host, error => {
    if (error) throw new gutil.PluginError('webpack-dev-server', error);
    gutil.log('[webpack-dev-server]', `${frontendUrl}/webpack-dev-server/index.html`);
    gutil.log(chalk`Checkout the development frontend at "{green ${frontendUrl}}".`);
  });
});

For the sake of completeness, here is my webpack configuration:

webpack.config.js

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

const dist = path.join(__dirname, 'dist', 'frontend');
const src = path.join(__dirname, 'src', 'main', 'frontend');

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    host: 'localhost',
    hot: true,
    overlay: {
      errors: true,
      warnings: true,
    },
    port: 8080,
    stats: 'errors-only',
    watchContentBase: true
  },
  entry: {
    [process.env.npm_package_name]: [path.join(src, 'index.tsx')],
  },
  mode: 'development',
  module: {
    rules: [
      {
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        test: /\.tsx?$/,
      },
    ]
  },
  output: {
    filename: `[name].bundle.js`,
    path: dist,
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: path.join(src, 'index.html'),
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
};

Did a little more digging, and from what I can tell Server.addDevServerEntrypoints still exists.

Looking at the source, addDevServerEntrypoints is a helper function for modifying the app entry point, very similar to our unshift code above.

One thing that addDevServerEntrypoints does differently is that require.resolve is returning a full system path to the client instead of using the string webpack-dev-server/client. At least in our case, that seems to break HMR.

It also might be related to the way that the domain is generated: seems to use the host (0.0.0.0) instead of the public address (localhost).

Was this page helpful?
0 / 5 - 0 ratings