Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack hot module replacement in server code

All the webpack examples I have looked at so far deal with client side hot module replacement, for example: this and this.

According to the webpack document, one can use EITHER webpack-dev-server OR middlewares (webpack-dev-webpack-dev-middleware and webpack-hot-middleware, along with webpack-hot-middleware/client in a config entry, and integrated into e.g. express js) to enable hot module replacement for client side codes

Is it possible to enable hot module replacement for server side codes? The document does shows an example

var requestHandler = require("./handler.js");
var server = require("http").createServer();
server.on("request", requestHandler);
server.listen(8080);

// check if HMR is enabled
if(module.hot) {
    // accept update of dependency
    module.hot.accept("./handler.js", function() {
        // replace request handler of server
        server.removeListener("request", requestHandler);
        requestHandler = require("./handler.js");
        server.on("request", requestHandler);
    });
}

The document is quite spare in explanation.

So the question is, how would hot module replacement be implemented in server side code without restarting the server? (At the moment, I have nodemon watching server side code to restart the server on file changes)

like image 246
Green Avatar asked Nov 10 '22 00:11

Green


1 Answers

Hot reloading server middleware bundled with Webpack is actually much easier than hot reloading client side bundles for two reasons:

  1. You don't have to handle server / client communication.
  2. Middleware is almost always necessarily stateless so you don't need to concern yourself with state preservation.

This means you can ignore all the moving parts associated with client side hot module reloading such as WebSockets as well as teaching your code to update itself through module.hot.accept / module.hot.dispose.

Here's an example:

// ./src/middleware.js
module.exports = (req, res) => {
    res.send('Hello World');
};
// webpack.config.js
const path = require('path');

module.exports = {
    target: 'node',
    entry: './src/middleware.js',
    output: {
        path: path.join(__dirname, './dist'),
        filename: 'middleware.js',
        libraryTarget: 'commonjs2'
    }
};
// ./src/index.js
const express = require('express');
const config = require('webpack.config.js');

const app = express();
const queue = [];
let latestMiddleware;

webpack(config).watch(() => {
    // re-require new middleware
    delete require.cache[require.resolve('./dist/middleware.js')]
    latestMiddleware = require('./dist/middleware.js');
    // pass buffered requests to latestMiddleware
    while (queue.length) latestMiddleware.apply(void 0, queue.shift());
});

app.use((req, res, next) => {
    if (latestMiddleware) {
        latestMiddleware(req, res, next);
        return;
    }
    queue.push([req, res, next]);
});

app.listen(6060);

As you can see the fact that there's no state to worry about means the latestMiddleware can simply reference the new bundled middleware without having to write custom logic to update other modules in the dependency graph.

Incidentally, this is the exact same technique used by webpack-hot-server-middleware, the only difference is webpack-hot-server-middleware is more geared towards hot reloading universal apps on the server.

like image 135
riscarrott Avatar answered Jan 04 '23 02:01

riscarrott