Search code examples
webpackwebpack-dev-serverwebpack-hmrwebpack-hot-client

HMR with webpack-dev-server doesn't reload (webpack 4)


I'm developing my new JS starter for static sites and I want to have a create-react-app-like architecture. All my webpack configuration is based on the create-react-app webpack config but using Webpack 4.

When I'm launching the start.js script, a new WebpackDevServer using node API is created with hot: true option, but the hot reloading is not working at all.

Here is my webpack config:

const webpack = require('webpack')

const autoprefixer = require('autoprefixer')
const path = require('path')
const paths = require('./paths')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const getClientEnvironment = require('./env')

// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
const publicPath = '/'

// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
const publicUrl = ''

// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl)

// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
  mode: process.env.NODE_ENV,
  entry: [require.resolve('./polyfills'), paths.appIndexJs],
  output: {
    pathinfo: true,
    filename: 'static/js/bundle.js',
    chunkFilename: 'static/js/[name].chunk.js',
    publicPath: publicPath,
    devtoolModuleFilenameTemplate: info =>
      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')
  },
  resolve: {
    modules: ['node_modules', paths.appNodeModules].concat(
      // It is guaranteed to exist because we tweak it in `env.js`
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),
    extensions: ['.js', '.json', '.jsx'],
    alias: {
      src: path.resolve(paths.appSrc),
      locales: path.resolve(paths.appSrc, 'locales'),
      app: path.resolve(paths.appSrc, 'app'),
      config: path.resolve(paths.appSrc, 'config'),
      components: path.resolve(paths.appSrc, 'components'),
      values: path.resolve(paths.appSrc, 'values'),
      utils: path.resolve(paths.appSrc, 'utils'),
      helpers: path.resolve(paths.appSrc, 'utils/helpers')
    }
  },
  module: {
    strictExportPresence: true,
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
                [
                  '@babel/preset-react',
                  {
                    pragma: 'h'
                  }
                ]
              ],
              cacheDirectory: true
            }
          },
          {
            loader: 'eslint-loader',
            options: { emitWarning: true }
          }
        ],
        exclude: /node_modules/,
        include: paths.appSrc
      },
      {
        test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
        loader: require.resolve('url-loader'),
        options: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]'
        }
      },
      {
        test: /\.scss$/,
        use: [
          require.resolve('style-loader'),
          {
            loader: require.resolve('css-loader'),
            options: {
              modules: true,
              localIdentName: '[local]___[hash:base64:5]',
              importLoaders: 2,
              alias: {
                src: path.resolve(__dirname, '../src')
              }
            }
          },
          {
            loader: 'sass-loader',
            options: {
              includePaths: ['./src'],
              data: '@import "config.scss";'
            }
          },
          {
            loader: require.resolve('postcss-loader'),
            options: {
              ident: 'postcss',
              plugins: () => [
                require('postcss-flexbugs-fixes'),
                autoprefixer({
                  browsers: [
                    '>1%',
                    'last 4 versions',
                    'Firefox ESR',
                    'not ie < 9'
                  ],
                  flexbox: 'no-2009'
                })
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml
    }),
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin(env.stringified),
    new webpack.HotModuleReplacementPlugin(),
    new CaseSensitivePathsPlugin(),
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
  ],
  node: {
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  },
  performance: {
    hints: false
  }
}

And here is my start script:

'use strict'

process.env.BABEL_ENV = 'development'
process.env.NODE_ENV = 'development'

const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const paths = require('../config/paths')

const config = require('../config/webpack.config.dev')

const compiler = webpack(config)

const server = new WebpackDevServer(compiler, {
  contentBase: paths.appPublic,
  publicPath: config.output.publicPath,
  inline: true,
  hot: true,
  watchContentBase: true,
  // quiet: true
  historyApiFallback: true,
  stats: { colors: true }
})

server.listen(8080, 'localhost', () => {
  console.log('Starting server on http://localhost:8080')
})

If you want to test the project, there's a repo on Github.


Solution

  • By adding two new entrypoints to my webpack dev config, HMR is working.

    The entrypoints are now:

    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/dev-server',
    require.resolve('./polyfills'),
    paths.appIndexJs
    

    You can check the referenced Issue on Github about this.