UNPKG

@cloud-cli/exec

Version:

Promise wrapper for child_process.spawn

91 lines (74 loc) 2.59 kB
import { EventEmitter } from 'events'; import { spawn, exec as sh } from 'child_process'; import { StringDecoder } from 'string_decoder'; export interface ExecOutput { ok: boolean; code: number; stdout: string; stderr: string; error?: Error; } export type ExecOptions = Parameters<typeof spawn>[2]; export type ShOptions = Parameters<typeof sh>[2]; export class Process extends EventEmitter { code: number = 0; completed = false; error: Error; get hasError() { return Boolean((this._errorBuffer.length && this.code !== 0) || this.code !== 0 || this.error); } constructor(protected childProcess: ReturnType<typeof spawn>) { super(); childProcess.stdout?.on('data', (data) => (this.stdout = data)); childProcess.stderr?.on('data', (data) => (this.stderr = data)); childProcess.on('exit', (code) => (this.code = code)); childProcess.on('close', () => this.complete()); childProcess.on('error', (error) => (this.error = error)); } set stdout(value: Buffer) { this._outputBuffer += this.stdoutDecoder.write(value); } set stderr(value: Buffer) { this._errorBuffer += this.stderrDecoder.write(value); } private _outputBuffer = ''; private _errorBuffer = ''; private stdoutDecoder = new StringDecoder(); private stderrDecoder = new StringDecoder(); complete(): void { this.completed = true; this._outputBuffer += this.stdoutDecoder.end(); this._errorBuffer += this.stderrDecoder.end(); this.emit('done', this.output); } get output(): ExecOutput { return { ok: !this.hasError && this.code === 0, code: this.error ? -1 : this.code, stdout: this._outputBuffer, stderr: this._errorBuffer, error: this.hasError ? new Error(this._errorBuffer) : undefined, }; } } export async function execString(command: string, options?: ShOptions): Promise<ExecOutput> { /* istanbul ignore next */ process.env.DEBUG && console.log(command, options); return execInternal(() => sh(command, options)); } export async function exec(command: string, args: string[] = [], options?: ExecOptions): Promise<ExecOutput> { /* istanbul ignore next */ process.env.DEBUG && console.log(command, args, options); return execInternal(() => spawn(command, args, options)); } function execInternal(runner: any): Promise<any> { return new Promise((resolve, reject) => { try { const childProcess = runner(); const state = new Process(childProcess); state.on('done', () => resolve(state.output)); } catch (error) { reject(error); } }); }