'How to auto-refresh the browser on .ejs changes when using express + webpack middlewares?

I have set up a server using express and the webpack-dev-middleware and webpack-hot-middleware that is currently accepting module replacements for the .js files.

Here is the setup I have currently:

server.js (backend)

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');

const config = require('./webpack.config.js');
const compiler = webpack(config);

const app = express();

let port = 3000;

app.set('views', 'views')
app.set('view engine', 'ejs');

app.use(
    express.static('public'),
    webpackDevMiddleware(compiler, {publicPath: config.output.publicPath}),
    webpackHotMiddleware(compiler)
);

app.get('/', (req, res) => {
    res.render('home-guest');
})

app.listen(port);

webpack.config.js

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

module.exports = {
    entry: [
        './public/app.js',
        'webpack-hot-middleware/client?reload=true'
    ],
    mode: 'development',
    output: {
        filename: 'bundled.js',
        path: path.resolve(__dirname, 'public'),
        publicPath: '/',
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin(),
    ]
}

app.js (frontend)

if(module.hot){
    module.hot.accept();
}

alert('Testing');

home-guest.ejs (view)

Welcome to the app.

<script src="bundled.js"></script>

All changes made to the app.js file are correctly using the HMR, replacing on the fly, without the need of a page refresh.

However, to see the changes on the home-guest.ejs files, I have to manually refresh the page.

I understand that in order to visualize the changes on an .ejs file, I do need to refresh. What I would I like to do is make it so the server automatically refreshes the page for me whenever it detects a change in the .ejs file.



Solution 1:[1]

Using webpack exclusively to refresh the browser has certain issues to it. My hybrid solution would be to use livereload with nodemon to load ejs files and webpack for other frontend files like browser javascript, css and/or scss:

Install livereload and connect-livereload

npm i -D livereload connect-livereload

Basic configurations

In your server.js

const express = require('express');
const livereload = require("livereload");
const connectLiveReload = require("connect-livereload");

//Livereload code
const liveReloadServer = livereload.createServer();
liveReloadServer.watch(path.join(__dirname, "public"));
liveReloadServer.server.once("connection", () => {
  setTimeout(() => {
    liveReloadServer.refresh("/");
  }, 100);
});
app.use(connectLiveReload());
//End of livereload code

const app = express();
//...... Rest of the code in server.js ........

What's happening here? Let's see line by line.

Creating the livereload server.

const liveReloadServer = livereload.createServer();

Watches the public folder for the webpack bundles changes. We just need to refresh the browser when webpack finishes the bundle's construction.

liveReloadServer.watch(path.join(__dirname, "public"));

Listens for nodemon's server reloads to refresh the browser.

liveReloadServer.server.once("connection", () => {
  setTimeout(() => {
    liveReloadServer.refresh("/");
  }, 100);
});

The middleware that actually refreshes the browser

app.use(connectLiveReload());

Aditional configurations

By default nodemon doesn't watches .ejs files or any frontend file for that matter. The problem with that is that we are refreshing the browser listening to nodemon's server restarts. So we need to tell it to watch for .ejs files' changes like so:

nodemon -e js,ejs,json

Also since we are watching our public files with livereload let's ignore the public folder altogether with nodemon.

nodemon.json

{
  "ignore": [
    "public"
  ]
}

That should do it. Good luck!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1