2

I'm trying to use webpack's Hot Module Replacement plugin. I've managed to randomly get it working, but it still isn't doing quite what I would hope it to.

Basically, I get no messages in my console that it's even active, though it's building without issue and file watching is working, as I get the messages webpack: bundle is now VALID and webpack: bundle is now INVALID when I update.

webpack, webpack-dev-server, and react-hot are all installed locally.

But in the browser's console, the only thing I see is:

Download the React DevTools for a better development experience: https://fb.me/react-devtools

I'm using Laravel to update my index file based on an environment variable and it is working just fine.

Here is the index.php file:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
    </head>
    <body>
        <div id="content"></div>

        @if(env("APP_HOTRELOAD"))
            <script src="http://localhost:8080/js/vendor.js"></script>
            <script src="http://localhost:8080/js/app.js"></script>
        @else
            <script src="js/vendor.js"></script>
            <script src="js/app.js"></script>
        @endif
    </body>
</html>

Here is the webpack config file (webpack.hot-reload.config.js):

var path = require("path");
var webpack = require("webpack");
var node_modules = path.resolve(__dirname, "node_modules");
var public_dir = path.resolve(__dirname, "public");

module.exports = {

    debug: (process.env.NODE_ENV === "production"),

    entry: {
        vendor: [
            "es5-shim",
            "es5-shim/es5-sham",
            "babel-core/polyfill",
            "babel-core/external-helpers",
            "react",
            "react-router-component"
        ],
        app: [
            "webpack-dev-server/client?http://localhost:8080",
            "webpack/hot/only-dev-server",
            path.resolve(__dirname, "resources/assets/js/index.js")
        ]
    },

    contentBase: public_dir,

    output: {
        path: path.resolve(public_dir, "js"),
        filename: "app.js",
        publicPath: "/"
    },

    plugins: [
        new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js"),
        //This is necessary for React to know whether it's supposed to strip out
        //addons and extra stuff when being minified.  Essentially, it becomes dead
        //code and webpack will take it out.
        new webpack.DefinePlugin({
            "process.env": {"NODE_ENV": JSON.stringify(process.env.NODE_ENV)}
        }),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
    ],

    module: {
        loaders: [
            {
                test: /\.(sa|c)ss$/,
                loader: "css!style!sass"
            },
            {
                test: /\.jsx?$/,
                exclude: /(node_modules|bower_components)/,
                loaders: [
                    "react-hot",
                    "strip-loader?strip[]=debug,strip[]=console.log,strip[]=console.error",
                    "babel-loader"
                ]
            }
        ]
    },

    resolve: {
        root: path.resolve(__dirname, "resources/assets/js"),
        extensions: ["", ".js", ".json"]
    }
};

In order to start the webpack-dev-server, I use a separate server.js file, executed by using node server.js:

var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.hot-reload.config');

new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    contentBase: config.contentBase,
    hot: true,
    historyApiFallback: true,
    quiet: false,
    noInfo: false,
    stats: {
        colors: true
    }
}).listen(8080, 'localhost', function (err, result) {
        if (err) {
            console.log(err);
        }
        console.log('Listening at localhost:8080');
    });

It seems to work randomly after waiting some time, but if I change a file or refresh the page manually, it seems to just break. I've tried using both Firefox and Chrome and it doesn't make a difference, so I'm thinking it's in the build.

What could be wrong?

Nathan Lutterman
  • 1,855
  • 4
  • 23
  • 38

1 Answers1

3

I figured it out. There was a comment about it on the page that notes how to use webpack-dev-server, but I managed to read over it.

If you look in my config you'll see:

...
output: {
    path: path.resolve(public_dir, "js"),
    filename: "app.js",
    **publicPath: "/"**
},
...

I misinterpreted the publicPath key and its path.

However, the example given in the docs shows:

module.exports = {
  entry: {
    app: ["./app/main.js"]
  },
  output: {
    path: "./build",
    publicPath: "/assets/",
    filename: "bundle.js"
  }
};

And states:

This modified bundle is served from memory at the relative path specified in publicPath (see API). It will not be written to your configured output directory. Where a bundle already exists at the same url path the bundle in memory will take precedence.

However, for this example, this bundle will be served from /, not /assets/ because further down, the content base is given as build/. There's nothing that notes that the directory where the scripts lie is possibly aliased to /assets/ at all, so that's why I placed the / path as the publicPath instead of the subdirectory my JS was actually being served from..

The docs note that:

To teach webpack to make requests (for chunk loading or HMR) to the webpack-dev-server you need to provide a full URL in the output.publicPath option.

So I changed:

    publicPath: "/"

to:

    publicPath: "http://localhost:8080/js/"

And now my files are being served up correctly. I added the /js/ because that's where I my JavaScript is served from on the actual server.

Nathan Lutterman
  • 1,855
  • 4
  • 23
  • 38