UNPKG

too

Version:

Combine multiple processes' Stdout/Stderr/SIGINT to keep them all foreground

87 lines (86 loc) 2.98 kB
import * as child_process from 'child_process'; import * as path from 'path'; import { lookpath } from 'lookpath'; import { CYAN } from './colors.js'; import { DefaultLogger } from './logger.js'; export class Command { oneliner; env; color; logger; stdout = process.stdout; stderr = process.stderr; label; command; args; constructor(oneliner, env = {}, color = CYAN, logger = new DefaultLogger(), label) { this.oneliner = oneliner; this.env = env; this.color = color; this.logger = logger; // FIXME: 乱暴な分割なので、スペースを含む引数に対応する const [c, ...a] = this.oneliner.split(" "); this.command = c; this.args = a; this.label = label || this.command; } async start() { const bin = await lookpath(this.command, { include: this.path() }); if (!bin) { return Promise.reject({ msg: `command not found: ${this.command}`, code: 127 }); } this.logger.accept(this.label, this.color, this.oneliner); const stream = child_process.spawn(this.command, this.args, { detached: false, env: { ...process.env, ...this.env, PATH: this.path().join(path.delimiter), }, shell: true, stdio: ["pipe", "pipe", "pipe"], }); stream.stdout.on("data", (chunk) => { this.logger.output(this.label, this.color, chunk, ""); }); stream.stderr.on("data", (chunk) => { this.logger.output(this.label, this.color, "", chunk); }); stream.on("close", (code, signal) => { if (code !== 0) { this.logger.output(this.label, this.color, "", `exit code ${code !== null ? code : signal}`); } }); return stream; } path() { const additionals = []; const cwd = process.cwd(); if ((path.join(cwd, "package.json"))) { additionals.push(path.join(cwd, "node_modules", ".bin")); } return [process.env.PATH || "", ...additionals]; } /** * Execute the command synchronously. */ async exec(env = this.env) { const bin = await lookpath(this.command, { include: this.path() }); if (!bin) { return Promise.reject({ msg: `command not found: ${this.command}`, code: 127 }); } this.logger.accept(this.label, this.color, this.oneliner); return new Promise((resolve, reject) => { child_process.exec(this.oneliner, { env: { ...process.env, ...env, }, }, (err, stdout, stderr) => { if (err) return reject(err); this.logger.output(this.label, this.color, stdout, stderr); return resolve(0); }); }); } }