@rspack/cli
Version:
CLI for rspack
94 lines (93 loc) • 3.65 kB
JavaScript
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 };