UNPKG

es-node-runner

Version:

Node runner that transpiles typescript or es modules using blazing fast ⚡ esbuild and restarts the process automatically on change. Suitable for node development server.

123 lines (122 loc) 3.79 kB
import {exec, execSync} from 'child_process'; import {spawn} from 'cross-spawn'; import DEBUG from 'debug'; import {logger} from './utils/index.js'; const debug = DEBUG('es-node-runner:runner'); let child, reRun; const isWin = process.platform === 'win32'; function run(spawnArgs) { debug('spawning new sub process'); const subProcess = spawn('node', spawnArgs, { stdio: [process.stdin, process.stdout, process.stderr], env: { ...process.env, }, }); reRun = run.bind(this, spawnArgs); if (subProcess.signalCode) { debug(`received '${subProcess.signalCode}' signal in sub process`); logger.error( 'The command execution failed because the process exited too early.' ); if (subProcess.signalCode === 'SIGKILL') { logger.error( 'This probably means the system ran out of memory or someone called "kill -9" on the process.\n' ); } else if (subProcess.signalCode === 'SIGTERM') { logger.error( 'Someone might have called `kill` or `killall`, or the system could be shutting down.\n' ); } debug('exiting sub process with code 1'); process.exit(1); } subProcess.on('spawn', () => { debug( child?.pid !== subProcess.pid ? 'sub process spawned' : 'sub process respawned' ); child = subProcess; }); subProcess.on('error', (error) => { debug(`error occurred in sub process ${subProcess.pid}`); logger.error(`[Error]: ${error.code} - ${error.message}\n`); if (error.code === 'ENOENT') { debug('exiting sub process with code 1'); process.exit(1); } else { throw error; } }); subProcess.once('exit', (code) => { debug(`sub process ${subProcess.pid} exited with code ${code}`); child = null; if (code > 0) { logger.error(`Sub process exited with code ${code}\n`); process.exit(0); } }); } function stop() { let status = -1; if (child && child.pid) { debug('terminating child processes'); if (isWin) { debug('executing windows kill cmd'); try { const terminationStatus = execSync( `powershell "(Invoke-CimMethod -InputObject (Get-CimInstance Win32_Process | ` + `Where-Object { $_.ParentProcessId -eq ${child.pid} }) -MethodName Terminate).ReturnValue"` ); status = terminationStatus.readUInt8(0); debug('child processes killed gracefully'); } catch (error) { debug('terminating child processes forcefully'); try { exec(`TASKKILL /PID ${child.pid} /F /T`); status = 48; } catch (error) { logger.error( 'Could not terminate cleanly. One or more child processes were still running. ' + `More info : ${error}\n` ); process.exit(1); } } } else { debug('executing non-windows kill cmd'); try { const buffer = execSync( `ps --ppid ${child.pid} -o pid | grep -oP '[0-9]+' | xargs -r` ); const pids = buffer.toString().match(/[0-9]+/g); if (Array.isArray(pids)) { pids.forEach((pid) => exec(`kill -TERM ${pid}`)); } child.kill(); status = 48; debug('child processes killed gracefully'); } catch (error) { logger.error( 'Could not terminate cleanly. One or more child processes were still running. ' + `More info : ${error}\n` ); process.exit(1); } } } else { status = 48; } return status; } function restart() { debug('restarting process after rebuild'); if (stop() === 48) { reRun(); } else { debug('could not restart process'); logger.error('Could not restart process\n'); } } export {run, restart};