Skip to content

Commit

Permalink
feat(app): Add hot reload functionality for HTML files and views folder
Browse files Browse the repository at this point in the history
  • Loading branch information
wajeht committed Nov 22, 2024
1 parent 3b20ab8 commit 63b66e7
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
"dont",
"Hasher",
"healthz",
"IIFE",
"knexfile",
"laravel",
"mailhot",
"Millis",
"multistream",
"Neue",
"nocheck",
"normies",
"pino",
"resave",
Expand Down
16 changes: 6 additions & 10 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import {
} from './middleware';
import ejs from 'ejs';
import cors from 'cors';
import path from 'node:path';
import express from 'express';
import flash from 'connect-flash';
import { router } from './router';
import { appConfig } from './config';
import compression from 'compression';
import expressLayouts from 'express-ejs-layouts';
import { reload } from './reload';

const app = express();

Expand All @@ -36,28 +36,24 @@ app.use(express.json({ limit: '100kb' }));

app.use(express.urlencoded({ extended: true, limit: '100kb' }));

app.use(
express.static(path.join(process.cwd(), 'public'), {
maxAge: '30d',
etag: true,
lastModified: true,
}),
);
app.use(express.static('./public', { maxAge: '30d', etag: true, lastModified: true }));

app.engine('html', ejs.renderFile);

app.set('view engine', 'html');

app.set('view cache', appConfig.env === 'production');

app.set('views', path.join(process.cwd(), 'src', 'views', 'pages'));
app.set('views', './src/views/pages');

app.set('layout', path.join(process.cwd(), 'src', 'views', 'layouts', 'public.html'));
app.set('layout', '../layouts/public.html');

app.use(expressLayouts);

app.use(appLocalStateMiddleware);

reload({ app, watch: [{ path: './src/views/pages', extensions: ['.html'] }] });

app.use(router);

app.use(notFoundMiddleware());
Expand Down
57 changes: 57 additions & 0 deletions src/reload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// @ts-nocheck

import fs from 'fs';

export function reload({ app, watch, options = {} }) {
if (process.env.NODE_ENV === 'production') return;

const pollInterval = options.pollInterval || 50;
const quiet = options.quiet || false;
let changeDetected = false;

watch.forEach(({ path: dir, extensions }) => {
const extensionsSet = new Set(extensions);

fs.watch(dir, { recursive: true }, (_, filename) => {
if (filename && extensionsSet.has(filename.slice(filename.lastIndexOf('.')))) {
if (!quiet) console.log('File changed:', filename);
changeDetected = true;
}
});
});

app.get('/wait-for-reload', (req, res) => {
const timer = setInterval(() => {
if (changeDetected) {
changeDetected = false;
clearInterval(timer);
res.send(); // Empty 200 OK
}
}, pollInterval);

req.on('close', () => clearInterval(timer));
});

const clientScript = `
<script>
(async function poll() {
try {
await fetch('/wait-for-reload');
location.reload();
} catch {
location.reload();
}
})();
</script>`;

app.use((req, res, next) => {
const originalRender = res.render;
res.render = function (view, options, callback) {
originalRender.call(this, view, options, (err, html) => {
if (err) return callback ? callback(err) : next(err);
res.send(html.replace('</head>', clientScript + '</head>'));
});
};
next();
});
}

0 comments on commit 63b66e7

Please sign in to comment.