import type { CommonLogger } from '@naturalcycles/js-lib' export interface RunScriptOptions { /** * @default false * Set to true to NOT call process.exit(0) after function is completed. * Currently it exists because of `jest --maxWorkers=1` behavior. To be investigated more.. */ noExit?: boolean /** * Default to `console` */ logger?: CommonLogger } const { DEBUG_RUN_SCRIPT } = process.env /** * Use it in your top-level scripts like this: * * runScript(async () => { * await lalala() * // my script goes on.... * }) * * Advantages: * - Works kind of like top-level await * - No need to add `void` * - No need to add `.then(() => process.exit()` (e.g to close DB connections) * - No need to add `.catch(err => { console.error(err); process.exit(1) })` * * This function is kept light, dependency-free, exported separately. * * Set env DEBUG_RUN_SCRIPT for extra debugging. */ export function runScript(fn: (...args: any[]) => any, opt: RunScriptOptions = {}): void { const { logger = console, noExit } = opt process.on('uncaughtException', err => { logger.error('uncaughtException:', err) }) process.on('unhandledRejection', err => { logger.error('unhandledRejection:', err) }) if (DEBUG_RUN_SCRIPT) { process.on('exit', code => logger.log(`process.exit event, code=${code}`)) process.on('beforeExit', code => logger.log(`process.beforeExit event, code=${code}`)) } // fake timeout, to ensure node.js process won't exit until runScript main promise is resolved const timeout = setTimeout(() => {}, 10000000) void (async () => { try { await fn() if (DEBUG_RUN_SCRIPT) logger.log(`runScript promise resolved`) if (!noExit) { setImmediate(() => process.exit(0)) } } catch (err) { logger.error('runScript error:', err) process.exitCode = 1 if (!noExit) { setImmediate(() => process.exit(1)) } } finally { clearTimeout(timeout) } })() }