UNPKG

villa

Version:

Promise utilities for async/await-ready environment.

198 lines (170 loc) 5.1 kB
// tslint:disable:unified-signatures import {ChildProcess} from 'child_process'; import {EventEmitter} from 'events'; import {Readable, Writable} from 'stream'; const {EventEmitter: EventEmitterConstructor} = safeRequire('events'); const {ChildProcess: ChildProcessConstructor} = safeRequire('child_process'); const { Readable: ReadableConstructor, Writable: WritableConstructor, } = safeRequire('stream'); import {awaitableCreators} from '../awaitable'; export type EventEmitterResultAssertion<T> = (result: any) => T; interface EventEmitterAwaitableOptions<T> { types: string[]; assertion?: EventEmitterResultAssertion<T>; } function getEventEmitterAwaitableOptions( emitter: EventEmitter, ): EventEmitterAwaitableOptions<any> { if (ChildProcessConstructor && emitter instanceof ChildProcessConstructor) { return { types: ['exit'], assertion(code: number): void { if (code !== 0) { throw new Error(`Invalid exit code ${code}`); } }, }; } else if ( (ReadableConstructor && emitter instanceof ReadableConstructor) || (WritableConstructor && emitter instanceof WritableConstructor) ) { return { types: ['close'], }; } else { throw new Error('Missing event types'); } } function eventEmitterAwaitableCreator<T>( emitter: EventEmitter, types: string | string[], errorEmitters?: EventEmitter[], ): Promise<T> | undefined; function eventEmitterAwaitableCreator<T>( emitter: EventEmitter, types: string | string[], assertion: EventEmitterResultAssertion<T>, errorEmitters?: EventEmitter[], ): Promise<T> | undefined; function eventEmitterAwaitableCreator( process: ChildProcess | Readable | Writable, errorEmitters?: EventEmitter[], ): Promise<void>; function eventEmitterAwaitableCreator( stream: Readable | Writable, errorEmitters?: EventEmitter[], ): Promise<void>; function eventEmitterAwaitableCreator<T>( emitter: EventEmitter, types: string | string[] | EventEmitter[] | undefined, assertion: EventEmitterResultAssertion<T> | EventEmitter[] | undefined = [], errorEmitters?: EventEmitter[] | undefined, ): Promise<T> | undefined { if (!(emitter instanceof EventEmitterConstructor)) { return undefined; } if (typeof types === 'string' || isStringArray(types)) { if (typeof types === 'string') { types = [types]; } if (Array.isArray(assertion)) { errorEmitters = assertion; // TODO: possibly a bug of TypeScript 2.2, adding ! temporarily. assertion = undefined!; } } else { errorEmitters = types; let options = getEventEmitterAwaitableOptions(emitter); types = options.types; // TODO: possibly a bug of TypeScript 2.2, adding ! temporarily. assertion = options.assertion!; } if (!errorEmitters) { errorEmitters = []; } errorEmitters.unshift(emitter); let promise = new Promise<any>((resolve, reject) => { for (let type of types as string[]) { emitter.on(type, onsuccess); } for (let emitter of errorEmitters!) { emitter.on('error', onerror); } function removeListeners() { for (let type of types as string[]) { emitter.removeListener(type, onsuccess); } for (let emitter of errorEmitters!) { emitter.removeListener('error', onerror); } } function onsuccess(value: T) { removeListeners(); resolve(value); } function onerror(error: any) { setImmediate(removeListeners); reject(error); } }); if (assertion) { promise = promise.then(assertion); } return promise; } /* istanbul ignore else */ if (EventEmitterConstructor) { awaitableCreators.push(eventEmitterAwaitableCreator); } function safeRequire(id: string): any { try { return require(id); } catch (error) { /* istanbul ignore next */ return {}; } } function isStringArray(object: any): object is string[] { return Array.isArray(object) && typeof object[0] === 'string'; } declare module '../index' { /** * Create a promise for an event emitter. */ function awaitable( emitter: EventEmitter, types: string | string[], errorEmitters?: EventEmitter[], ): Promise<void>; function awaitable<T>( emitter: EventEmitter, types: string | string[], errorEmitters?: EventEmitter[], ): Promise<T>; function awaitable<T>( emitter: EventEmitter, types: string | string[], assertion: EventEmitterResultAssertion<T>, errorEmitters?: EventEmitter[], ): Promise<T>; /** * Create a promise for a `ChildProcess` object. * @param process The process to listen on 'exit' and 'error' events for * fulfillment or rejection. */ function awaitable( process: ChildProcess, errorEmitters?: EventEmitter[], ): Promise<void>; /** * Create a promise for a stream. * @param stream The stream to listen on 'close' and 'error' events for * fulfillment or rejection. */ function awaitable( stream: Readable | Writable, errorEmitters?: EventEmitter[], ): Promise<void>; }