7ohm
Version:
Cross-platform concurrency without external dependencies
73 lines (56 loc) • 2.08 kB
JavaScript
const { spawn } = require('child_process')
const { name, version } = require('../package.json')
const PM_SCRIPTS_SHORTCUT_REGEX = /^(npm|pnpm|yarn):\w+$/
const PM_SCRIPT_DIVIDER = ':'
/**
* @param {*} commands - list of commands to execute
* @param {*} onComplete - called when all commands exited by themselves
* @returns a function that kills all running processes regardless of their state
*/
function executeCommands(commands, onComplete) {
let nRunning = commands.length
const processes = commands.map((command) => {
const [ executable, ...options ] = expandCommand(command).split(' ').map(c => c.trim())
const cmd = spawn(executable, options, { shell: true, stdio: 'inherit' })
cmd.on('close', (code) => {
console.log(`${command} exited with code ${code}`);
if(--nRunning < 1) onComplete()
})
return cmd
})
return (signal) => processes.forEach((p) => p.kill(signal))
}
function expandCommand(command) {
const trimmed = command.trim()
if (!trimmed.match(PM_SCRIPTS_SHORTCUT_REGEX)) return trimmed;
const [pm, script] = trimmed.split(PM_SCRIPT_DIVIDER);
return `${pm} run ${script}`;
}
module.exports = executeCommands
/* Command line execution code */
const HELP_TEXT = `${name} (v: ${version}) usage: ${name} command1 command2 ...`
const HELP_TRIGGERS = ['-h', '--help']
const TERMINATE_SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP']
function main() {
const commands = process.argv.slice(2);
if (commands.length === 0 || commands.some((cmd) => HELP_TRIGGERS.includes(cmd))) {
console.log(HELP_TEXT)
process.exit()
}
const cmdText = commands.map((cmd) => `\`${cmd}'`).join(', ')
console.log(`\n[${name}: executing listed commands]:`, cmdText, '\n')
const terminate = executeCommands(commands, () => {
console.log(`\n[${name}: all commands finished execution, exiting]`)
process.exit()
})
TERMINATE_SIGNALS.forEach((signal) => {
process.on(signal, () => {
terminate(signal)
process.exit()
})
})
}
if (require.main === module) {
main()
}