How to achieve server HMR without nodemon

Current process:

  1. Start client dev server.
  2. Start server webpack in watch mode.
  3. Listen to changes with nodemon.

Current behavior:

  • When I update the client scripts, the app updates without reloading the page.
  • When I update the server scripts, the app is up to date when I manually reload the page, which is OK for me.

In my understading, the main drawback of this config is that nodemon restarts the server each time I update the server bundle. As my app grows, I’m afraid the reload would get slower and slower.

I’d like to know if I could update the server bundle without restarting the server each time.

  "scripts": {
    "start": "node start.js & nodemon ./dist/server.js"
  },

start.js

const webpack = require('webpack');
const webpackDevServer = require('webpack-dev-server');
const clientConfig = require('./config.client.js');
const serverConfig = require('./config.server.js');

const clientCompiler = webpack(clientConfig);
const clientDevServerOptions = { ...clientConfig.devServer };
const clientServer = new webpackDevServer(clientDevServerOptions, clientCompiler);

clientServer.startCallback(() => {
    const serverCompiler = webpack(serverConfig); 
    serverCompiler.watch({}, (err, stats) => {});
})

config.client.js

const path = require("path");
const webpack = require("webpack");
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
 
module.exports = {
  name: 'client',
  target: 'web',
   mode: 'development',
   entry: [
     './src/client.js'
   ],
   devServer: {
     headers: { 'Access-Control-Allow-Origin': '*' },
    host: '0.0.0.0',
    port: 3001,
   },
   plugins: [
       new WebpackManifestPlugin({ writeToFileEmit: true }),
   ],
   output: {
     filename: 'client.js',
     path: path.resolve(__dirname, 'dist'),
     publicPath: 'http://0.0.0.0:3001/',
   },
 };

server.config.js

const path = require("path");
const nodeExternals = require('webpack-node-externals');
module.exports = {
  name: 'server',
  target: 'node',
  mode: 'development',
  node: {
    global: false,
    __filename: false,
    __dirname: false,
  },
   entry: [
     './src/server.js'
    ],
  externalsPresets: { node: true },
   externals: [nodeExternals()],
   output: {
     filename: 'server.js',
     path: path.resolve(__dirname, 'dist'),
     publicPath: '/',
   },
 };

src/client.js

const div = document.createElement('div');
div.innerHTML = 'Hello world.....';
document.getElementById('root').appendChild(div);

src/server.js


const fastify = require('fastify')
const manifest = require('../dist/manifest.json');

const app = fastify()
  .get('/', async (request, reply) => {
    return reply
        .status(200)
        .type('text/html')
        .send(`
    <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      <div id="root"></div>
          <script src="${manifest['main.js']}"></script>
      </body>
    </html>
    `);
  });

app.listen(3000).then(() => {
  console.log('Server started.');
});