UNPKG

@rspack/cli

Version:
94 lines (93 loc) 3.65 kB
import node_process from "node:process"; const asyncCallbacks = new Set(); const callbacks = new Set(); let isCalled = false; let isRegistered = false; async function flushStdio() { const flush = (stream)=>new Promise((resolve)=>{ if (!stream || !stream.writable || stream.writableEnded || stream.destroyed) return void resolve(); const onError = ()=>{ stream.off('error', onError); resolve(); }; stream.once('error', onError); try { stream.write('', ()=>{ stream.off('error', onError); resolve(); }); } catch { stream.off('error', onError); resolve(); } }); const timeout = new Promise((resolve)=>{ setTimeout(resolve, 1000); }); await Promise.race([ Promise.all([ flush(node_process.stdout), flush(node_process.stderr) ]), timeout ]); } async function exit(shouldManuallyExit, isSynchronous, signal) { if (isCalled) return; isCalled = true; if (asyncCallbacks.size > 0 && isSynchronous) console.error("SYNCHRONOUS TERMINATION NOTICE: When explicitly exiting the process via process.exit or via a parent process, asynchronous tasks in your exitHooks will not run. Either remove these tasks, use gracefulExit() instead of process.exit(), or ensure your parent process sends a SIGINT to the process running this code."); let exitCode = 0; if (signal > 0) exitCode = 128 + signal; else if ('number' == typeof node_process.exitCode || 'string' == typeof node_process.exitCode) exitCode = node_process.exitCode; const done = (force = false)=>{ if (true === force || true === shouldManuallyExit) node_process.exit(exitCode); }; for (const callback of callbacks)callback(exitCode); if (isSynchronous) return void done(); const promises = []; let forceAfter = 0; for (const [callback, wait] of asyncCallbacks){ forceAfter = Math.max(forceAfter, wait); promises.push(Promise.resolve(callback(exitCode))); } const asyncTimer = forceAfter > 0 ? setTimeout(()=>{ done(true); }, forceAfter) : void 0; await Promise.all(promises); clearTimeout(asyncTimer); await flushStdio(); done(); } function addHook(options) { const { onExit, wait, isSynchronous } = options; const asyncCallbackConfig = [ onExit, wait ]; if (isSynchronous) callbacks.add(onExit); else asyncCallbacks.add(asyncCallbackConfig); if (!isRegistered) { isRegistered = true; node_process.once('beforeExit', exit.bind(void 0, true, false, -128)); node_process.once('SIGINT', exit.bind(void 0, true, false, 2)); node_process.once('SIGTERM', exit.bind(void 0, true, false, 15)); node_process.once('exit', exit.bind(void 0, false, true, 0)); node_process.on('message', (message)=>{ if ('shutdown' === message) exit(true, true, -128); }); } return ()=>{ if (isSynchronous) callbacks.delete(onExit); else asyncCallbacks.delete(asyncCallbackConfig); }; } function asyncExitHook(onExit, options = {}) { if ('function' != typeof onExit) throw new TypeError('onExit must be a function'); if (!('number' == typeof options.wait && options.wait > 0)) throw new TypeError('wait must be set to a positive numeric value'); return addHook({ onExit, wait: options.wait, isSynchronous: false }); } export { asyncExitHook };