Skip to content

Instantly share code, notes, and snippets.

@ggoodman
Created November 22, 2020 02:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ggoodman/879eba4bac7b9c88bb67f0b8095bef4f to your computer and use it in GitHub Desktop.
Save ggoodman/879eba4bac7b9c88bb67f0b8095bef4f to your computer and use it in GitHub Desktop.
Bare-bones nodemon + esbuild hybrid for hot-reloading a process
//@ts-check
'use strict';
const ChildProcess = require('child_process');
const Events = require('events');
const Path = require('path');
const { watch } = require('chokidar');
const { startService } = require('esbuild');
const Pino = require('pino');
async function buildApi(logger) {
process.chdir(__dirname);
const service = await startService();
const watcher = watch(Path.resolve(__dirname, './src/**/*'), {
interval: 20,
});
const start = Date.now();
const build = await service.build({
bundle: true,
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
},
format: 'cjs',
incremental: true,
mainFields: ['module', 'main'],
platform: 'node',
write: true,
target: 'node10',
entryPoints: ['./src/index.ts'],
outdir: './dist',
});
logger.info({ latency: Date.now() - start, warnings: build.warnings }, 'initial build complete');
const rebuild = async () => {
const start = Date.now();
try {
const rebuild = await build.rebuild();
logger.info({ latency: Date.now() - start, warnings: rebuild.warnings }, 'rebuild complete');
await postBuild();
} catch (err) {
logger.error({ err }, 'error while attempting to rebuild');
}
};
const postBuild = async () => {
if (child && !Number.isInteger(child.exitCode)) {
logger.info({ childPid: child.pid }, 'sending SIGKILL to managed process');
child.kill('SIGTERM');
const [exitCode] = await Events.once(child, 'exit');
logger.info({ exitCode, childPid: child.pid }, 'managed process exited');
}
child = ChildProcess.spawn('node', [Path.join(__dirname, 'dist/index.js')], {
stdio: ['ignore', 'inherit', 'inherit'],
});
logger.info({ childPid: child.pid }, 'managed process spawned');
};
/** @type {import('child_process').ChildProcess | undefined} */
let child = undefined;
let queue = postBuild();
watcher.on('all', (e) => {
queue = queue.then(rebuild);
});
}
async function main(logger) {
await buildApi(logger);
}
const logger = Pino({
prettyPrint: process.env.NODE_ENV !== 'production',
});
main(logger).catch((err) => {
logger.fatal({ err }, 'uncaught exception');
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment