Repository URL to install this package:
|
Version:
3.0.0-beta.1 ▾
|
import {
ChildProcess,
ExecOptions,
ExecFileOptions,
SpawnOptions,
ForkOptions,
} from 'child_process'
import * as child_process from 'child_process'
export interface Output {
stdout?: string | Buffer
stderr?: string | Buffer
}
export type ErrorWithOutput = Error &
Output & {
code?: number
signal?: string
}
type ChildProcessPromise = ChildProcess & Promise<Output>
function joinChunks(
chunks: ReadonlyArray<string | Buffer>,
encoding: string
): string | Buffer {
if (chunks[0] instanceof Buffer) {
const buffer = Buffer.concat(chunks as any)
if (encoding) {
return buffer.toString(encoding as any)
}
return buffer
}
return chunks.join('')
}
export function promisifyChildProcess(
child: ChildProcess,
options: { encoding?: string } = {}
): ChildProcessPromise {
const _promise = new Promise(
(
resolve: (result: Output) => void,
reject: (err: ErrorWithOutput) => void
) => {
const stdoutChunks: Array<string | Buffer> = []
const stderrChunks: Array<string | Buffer> = []
if (child.stdout) {
child.stdout.on('data', data => stdoutChunks.push(data))
}
if (child.stderr) {
child.stderr.on('data', data => stderrChunks.push(data))
}
child.on('error', reject)
function done(code: number, signal: string) {
// as any because ignoring assignment with this async promise
let error: ErrorWithOutput = undefined as any
if (code !== null && code !== 0) {
error = new Error(`Process exited with code ${code}`)
} else if (signal !== null) {
error = new Error(`Process was killed with ${signal}`)
}
const output: Output = {}
if (child.stdout) {
output.stdout = joinChunks(stdoutChunks, options.encoding as string)
}
if (child.stderr) {
output.stderr = joinChunks(stderrChunks, options.encoding as string)
}
if (error) {
if (code !== null) {
error.code = code
}
if (signal !== null) {
error.signal = signal
}
if (output.stdout) {
error.stdout = output.stdout
}
if (output.stderr) {
error.stderr = output.stderr
}
reject(error)
} else {
resolve(output)
}
}
child.on('close', done)
child.on('exit', done)
}
)
return Object.create(child, {
then: { value: _promise.then.bind(_promise) },
catch: { value: _promise.catch.bind(_promise) },
}) as any
}
export function spawn(
command: string,
args?: ReadonlyArray<string> | SpawnOptions,
options?: SpawnOptions
): ChildProcessPromise {
return promisifyChildProcess(
child_process.spawn(command, args as ReadonlyArray<string>, options),
(Array.isArray(args) ? options : args) as any
)
}
export function fork(
module: string,
args?: ReadonlyArray<string> | ForkOptions,
options?: ForkOptions
): ChildProcessPromise {
return promisifyChildProcess(
child_process.fork(module, args as ReadonlyArray<string>, options),
(Array.isArray(args) ? options : args) as any
)
}
function promisifyExecMethod(method: any): any {
return (...args: any[]): ChildProcessPromise => {
// as any because ignoring assignment with this async promise
let child: ChildProcess = undefined as any
const _promise = new Promise(
(
resolve: (output: Output) => void,
reject: (error: ErrorWithOutput) => void
) => {
child = method(
...args,
(
err: ErrorWithOutput,
stdout: Buffer | string,
stderr: Buffer | string
) => {
if (err) {
err.stdout = stdout
err.stderr = stderr
reject(err)
} else {
resolve({ stdout, stderr })
}
}
)
}
)
if (!child) {
throw new Error('unexpected error: child has not been initialized')
}
return Object.create(child as any, {
then: { value: _promise.then.bind(_promise) },
catch: { value: _promise.catch.bind(_promise) },
}) as any
}
}
export const exec: (
command: string,
options?: ExecOptions
) => ChildProcessPromise = promisifyExecMethod(child_process.exec)
export const execFile: (
file: string,
args?: string[] | ExecFileOptions,
options?: ExecOptions
) => ChildProcessPromise = promisifyExecMethod(child_process.execFile)