Search code examples
webpackwebpack-dev-serverreact-hot-loaderhapi.js

Dev server not hot-reloading, failing to build


I'm working on a react/redux app, served up locally with a npm-piped hapi.js backend on port:3000, and a webpack-dev-server running on port:3001;

I have a couple api routes on teh back to serve up static files, and then I hit asset files out of my build/public directory with a {param*} rule. To make that work, I have a proxy on the WebpackDevServer that forwards requests back to port:3000

I have CSSModules performing a build of .scss, and there are a couple of other loaders in place.

When I first set this up, it worked as expected. I could add files, save content, perform the build and the HMR would do its thing, and update the dom. Worked great. At some point, this stopped working very well. The backend on :3000 does a rebuild and reload, while the front end on :3001 is receiving an error like so:

[HMR] Checking for updates on the server...
bundle.js:26 GET http://localhost:3001/dist/ee2fe9b049ee40ff922c.hot-update.json 404 (Not Found)hotDownloadManifest @ bundle.js:26hotCheck @ bundle.js:245check @ bundle.js:8080(anonymous function) @ bundle.js:8138
bundle.js:8095 [HMR] Cannot find update. Need to do a full reload!
bundle.js:8096 [HMR] (Probably because of restarting the webpack-dev-server)

I notice there is a reference to :8080 in there (the webpack-dev-server default), but my references are all to :3000/1.

When this stack was working well - I could save the server.js and the hapi server would restart itself (due to the npm piping), and the webpack build would go as expected. Currently the build failing intermittently from server.js, and I'm must manually $ webpack and reload the browser to trigger the build and successful refresh. Which is obviously defeating the point.

The Important Bits :

server.js

// ... hapi.js settings

// Dev server / HMR
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('../../webpack.config.js');

if (!isProduction){
  new WebpackDevServer(webpack(config), {
    publicPath: 'dist',
    hot: true,
    historyApiFallback: true,
    proxy: {
      "*": 'http://localhost:3000'
    },
    quiet: false,
    stats: { colors: true }
  }).listen(3001, 'localhost', (err, result) => {
    if (err){
      console.log(err);
    }
    console.log('WebpackDevServer[localhost::3001]');
  });
}

webpack.config.js

// imports
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const validate = require('webpack-validator');
const path = require('path');

// paths
const rootPath = path.resolve(__dirname, 'client', 'src');

// configger the almighty webpack
const config = {
  entry: [
    'webpack-dev-server/client?http://localhost:3001',
    'webpack/hot/only-dev-server',
    path.resolve(rootPath, 'index.jsx')
  ],
  resolve: {
    extensions: ['', '.js', '.jsx'],
    root: rootPath
  },
  output: {
    path: path.resolve(__dirname, 'public', 'dist'),
    publicPath: '/dist/',
    filename: 'bundle.js',
    sourceMapFilename: 'bundle.map'
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: [path.resolve(__dirname, 'node_modules')],
        loader: 'react-hot!babel',
        include: rootPath
      }, {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass'),
        include: rootPath
      }, {
        test: /\.(png|jpg|gif)$/,
        loader: 'file?name=/images/[name].[ext]',
        include: rootPath
      }
    ]
  },
  devtool: '#source-map',
  devServer: {
    contentBase: '/public',
    hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    new ExtractTextPlugin('styles.css')
  ]
};

module.exports = validate(config);

Been tinkering with all the settings, so I might of munged what had been working. But it seems this should be functioning as expected.

Any insight into this config stack would be appreciated. Project source : github

Best -


Solution

  • Welp. A bit of tinkering, if anyone else has this issue.

    I modified the server.js code to handle all the dev server configurations, which now means that if I view the site at :3001, saves do perform a rebuild into memory, and those are served up on the fly. Which is good.

    As I understand it, the below WebpackDevServer config will not actually rebuild new files (as the docs seem to indicate). I still have to $ webpack manually to actually build the files. I doubt this is the correct behavior, but if I'm getting a live reload, then that's pretty good. I just have to stay on :3001.

    server.js

    // Dev server / HMR
    const webpack = require('webpack');
    const WebpackDevServer = require('webpack-dev-server');
    const config = require('../../webpack.config.js');
    const compiler = webpack(config);
    
    new WebpackDevServer(compiler, {
      port: 3001,
      publicPath: '/dist/',
      contentBase: 'dist/',
      historyApiFallback: true,
      inline: true,
      hot: false,
      quiet: false,
      stats: { colors: true },
      proxy: {
        '*': 'http://localhost:3000'
      }
    }).listen(3001, 'localhost', (err, result) => {
      if (err){
        console.log(err);
      }
      console.log('WebpackDevServer[localhost::3001]');
    });
    

    webpack.config.js

    // imports
    const webpack = require('webpack');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    const validate = require('webpack-validator');
    const path = require('path');
    
    // paths
    const rootPath = path.resolve(__dirname, 'client', 'src');
    
    // configger the almighty webpack
    const config = {
      entry: [
        'webpack-dev-server/client?http://localhost:3001',
        'webpack/hot/only-dev-server',
        path.resolve(rootPath, 'index.jsx')
      ],
      resolve: {
        extensions: ['', '.js', '.jsx'],
        root: rootPath
      },
      output: {
        path: path.resolve(__dirname, 'public', 'dist'),
        publicPath: '/dist/',
        filename: 'bundle.js',
        sourceMapFilename: 'bundle.map'
      },
      module: {
        loaders: [
          {
            test: /\.jsx?$/,
            exclude: [path.resolve(__dirname, 'node_modules')],
            loader: 'react-hot!babel',
            include: rootPath
          }, {
            test: /\.scss$/,
            loader: ExtractTextPlugin.extract('css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass'),
            include: rootPath
          }, {
            test: /\.(png|jpg|gif)$/,
            loader: 'file?name=/images/[name].[ext]',
            include: rootPath
          }
        ]
      },
      devtool: '#source-map',
      plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),
        new ExtractTextPlugin('styles.css')
      ]
    };
    
    module.exports = validate(config);