import * as nodeChildProcess from "child_process" ; export interface IExecResult { process : nodeChildProcess . ChildProcess ; outcome : Promise < string > ; } export interface ISpawnResult { spawnedProcess : nodeChildProcess . ChildProcess ; stdin : NodeJS . WritableStream ; stdout : NodeJS . ReadableStream ; stderr : NodeJS . ReadableStream ; outcome : Promise < void > ; } interface IExecOptions { cwd ? : string ; stdio ? : any ; env ? : any ; encoding ? : string ; timeout ? : number ; maxBuffer ? : number ; killSignal ? : string ; } interface ISpawnOptions { cwd ? : string ; stdio ? : any ; env ? : any ; detached ? : boolean ; } export class ChildProcess { public static ERROR_TIMEOUT_MILLISECONDS = 300 ; private childProcess : typeof nodeChildProcess ; constructor ( { childProcess = nodeChildProcess } = { } ) { this . childProcess = childProcess ; } public exec ( command , options = { } ) { let outcome ; let process ; return new Promise < IExecResult > ( resolveRes => { outcome = new Promise < string > ( ( resolve , reject ) => { process = this . childProcess . exec ( command , options , ( error , stdout , stderr ) => { if ( error ) { reject ( error ) ; } else { resolve ( stdout ) ; } } ) ; } ) ; resolveRes ( { process : process , outcome : outcome } ) ; } ) ; } public execToString ( command , options = { } ) { return this . exec ( command , options ) . then ( result => result . outcome . then ( stdout => stdout . toString ( ) ) ) ; } public execFileSync ( command , args = [ ] , options = { } ) { return this . childProcess . execFileSync ( command , args , options ) ; } public spawn ( command , args = [ ] , options = { } , showStdOutputsOnError = false ) { const spawnedProcess = this . childProcess . spawn ( command , args , options ) ; let outcome = new Promise ( ( resolve , reject ) => { spawnedProcess . once ( "error" , ( error ) => { reject ( error ) ; } ) ; const stderrChunks = [ ] ; const stdoutChunks = [ ] ; spawnedProcess . stderr . on ( "data" , data => { stderrChunks . push ( data . toString ( ) ) ; } ) ; spawnedProcess . stdout . on ( "data" , data => { stdoutChunks . push ( data . toString ( ) ) ; } ) ; spawnedProcess . once ( "exit" , ( code ) => { if ( code === 0 ) { resolve ( ) ; } else { const commandWithArgs = command + " " + args . join ( " " ) ; if ( showStdOutputsOnError ) { let details = "" ; if ( stdoutChunks . length > 0 ) { details = details . concat ( `\n\tSTDOUT: ${ stdoutChunks [ stdoutChunks . length - 1 ] }` ) ; } if ( stderrChunks . length > 0 ) { details = details . concat ( `\n\tSTDERR: ${ stderrChunks . join ( "\n\t" ) }` ) ; } if ( details === "" ) { details = "STDOUT and STDERR are empty!" ; } reject ( `Command '${ commandWithArgs }' failed with details: ${ details }` ) ; } else { reject ( `Command '${ commandWithArgs }' failed with code: ${ code }` ) ; } } } ) ; } ) ; return { spawnedProcess : spawnedProcess , stdin : spawnedProcess . stdin , stdout : spawnedProcess . stdout , stderr : spawnedProcess . stderr , outcome : outcome , } ; } }