Search code examples
javascriptwebpacknode-webkitwebpack-hmrhot-module-replacement

Webpack HMR doesn't work with Node-Webkit/NW.js


What I have

I created a small Webpack HMR Hello World with the following config file:

const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    app: path.join(__dirname, 'app/index.js'),
  },
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'app.js',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: [
          'babel-loader',
        ],
        exclude: /node_modules/
      },
    ],
  },
  plugins: [
    new webpack.NamedModulesPlugin(),
  ],
};

Then I run webpack-dev-server from an npm script and serve the file under http://localhost:8080/app.js. I include this file in my index.html and everything (including HMR) works fine in the browser.

The problem

I installed NW.js (Node-Webkit) via npm and set this index.html as an entry point for the main property in package.json. The application works properly but when I edit a file, HMR doesn't happen. Why it doesn't work in NW.js when it works in the browser?


Solution

  • tl;dr

    Add target: 'node-webkit', to your webpack.config.js.

    In details (behind the scenes)

    As the Webpack 1 and Webpack 2 documentation shows, you have to set a target config option because different environments work differently. For example, for the node-webkit option the documentation states:

    Compile for usage in webkit, uses jsonp chunk loading but also supports build in node.js modules plus require(“nw.gui”) (experimental)

    Also, for node it states this:

    In the example above, using node webpack will compile for usage in a Node.js-like environment (uses Node.js require to load chunks and not touch any built in modules like fs or path).

    Multiple targets

    Keep in mind that because of these differences, after you set node-webkit as your target, the web version isn't going to work in you browser. If you want to develop in both environment you have to create an isomorphic library by introducing multiple configurations with their own output. Just how the Webpack 2 Multiple Targets documentation does it:

    var path = require('path');
    var serverConfig = {
      target: 'node',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'lib.node.js'
      }
      //…
    };
    
    var clientConfig = {
      target: 'web', // <=== can be omitted as default is 'web'
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'lib.js'
      }
      //…
    };
    
    module.exports = [ serverConfig, clientConfig ];