Skip to content

Commit 3b9afe1

Browse files
authored
fix: mixin ProcessOutput data to promisified pipe value (google#954)
continues google#949
1 parent 4bb470b commit 3b9afe1

File tree

4 files changed

+54
-37
lines changed

4 files changed

+54
-37
lines changed

src/core.ts

+29-28
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
once,
4949
parseDuration,
5050
preferLocalBin,
51+
proxyOverride,
5152
quote,
5253
quotePowerShell,
5354
} from './util.js'
@@ -328,12 +329,12 @@ export class ProcessPromise extends Promise<ProcessOutput> {
328329

329330
// Essentials
330331
pipe(dest: TemplateStringsArray, ...args: any[]): ProcessPromise
331-
pipe<D extends Writable>(dest: D): D & PromiseLike<D>
332+
pipe<D extends Writable>(dest: D): D & PromiseLike<ProcessOutput & D>
332333
pipe<D extends ProcessPromise>(dest: D): D
333334
pipe(
334335
dest: Writable | ProcessPromise | TemplateStringsArray,
335336
...args: any[]
336-
): (Writable & PromiseLike<Writable>) | ProcessPromise {
337+
): (Writable & PromiseLike<ProcessPromise & Writable>) | ProcessPromise {
337338
if (isStringLiteral(dest, ...args))
338339
return this.pipe($({ halt: true })(dest as TemplateStringsArray, ...args))
339340
if (isString(dest))
@@ -372,7 +373,8 @@ export class ProcessPromise extends Promise<ProcessOutput> {
372373
return dest
373374
}
374375
from.once('end', () => dest.emit('end-piped-from')).pipe(dest)
375-
return promisifyStream(dest, this)
376+
return promisifyStream(dest, this) as Writable &
377+
PromiseLike<ProcessPromise & Writable>
376378
}
377379

378380
abort(reason?: string) {
@@ -867,32 +869,31 @@ export function log(entry: LogEntry) {
867869
}
868870
}
869871

870-
export const promisifyStream = <S extends Writable>(
872+
const promisifyStream = <S extends Writable>(
871873
stream: S,
872-
from?: ProcessPromise
873-
): S & PromiseLike<S> =>
874-
new Proxy(stream as S & PromiseLike<S>, {
875-
get(target, key) {
876-
if (key === 'run') return from?.run.bind(from)
877-
if (key === 'then') {
878-
return (res: any = noop, rej: any = noop) =>
879-
new Promise((_res, _rej) =>
880-
target
881-
.once('error', (e) => _rej(rej(e)))
882-
.once('finish', () => _res(res(target)))
883-
.once('end-piped-from', () => _res(res(target)))
874+
from: ProcessPromise
875+
): S & PromiseLike<ProcessOutput & S> =>
876+
proxyOverride(stream as S & PromiseLike<ProcessOutput & S>, {
877+
then(res: any = noop, rej: any = noop) {
878+
return new Promise((_res, _rej) =>
879+
stream
880+
.once('error', (e) => _rej(rej(e)))
881+
.once('finish', () =>
882+
_res(res(proxyOverride(stream, (from as any)._output)))
884883
)
885-
}
886-
const value = Reflect.get(target, key)
887-
if (key === 'pipe' && typeof value === 'function') {
888-
return function (...args: any) {
889-
const piped = value.apply(target, args)
890-
piped._pipedFrom = from
891-
return piped instanceof ProcessPromise
892-
? piped
893-
: promisifyStream(piped, from)
894-
}
895-
}
896-
return value
884+
.once('end-piped-from', () =>
885+
_res(res(proxyOverride(stream, (from as any)._output)))
886+
)
887+
)
888+
},
889+
run() {
890+
return from.run()
891+
},
892+
_pipedFrom: from,
893+
pipe(...args: any) {
894+
const piped = stream.pipe.apply(stream, args)
895+
return piped instanceof ProcessPromise
896+
? piped
897+
: promisifyStream(piped as Writable, from)
897898
},
898899
})

src/util.ts

+13
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,16 @@ export const once = <T extends (...args: any[]) => any>(fn: T) => {
449449
return (result = fn(...args))
450450
}
451451
}
452+
453+
export const proxyOverride = <T extends object>(
454+
origin: T,
455+
...fallbacks: any
456+
): T =>
457+
new Proxy(origin, {
458+
get(target: T, key) {
459+
return (
460+
fallbacks.find((f: any) => key in f)?.[key] ??
461+
Reflect.get(target as T, key)
462+
)
463+
},
464+
}) as T

test-d/core.test-d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ expectType<ProcessPromise>(p.nothrow())
2727
expectType<ProcessPromise>(p.quiet())
2828
expectType<ProcessPromise>(p.pipe($`cmd`))
2929
expectType<ProcessPromise>(p.pipe`cmd`)
30-
expectType<typeof process.stdout & PromiseLike<typeof process.stdout>>(
31-
p.pipe(process.stdout)
32-
)
30+
expectType<
31+
typeof process.stdout & PromiseLike<ProcessOutput & typeof process.stdout>
32+
>(p.pipe(process.stdout))
3333
expectType<ProcessPromise>(p.stdio('pipe'))
3434
expectType<ProcessPromise>(p.timeout('1s'))
3535
expectType<Promise<void>>(p.kill())

test/core.test.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -475,13 +475,21 @@ describe('core', () => {
475475
const p = $`echo "hello"`
476476
.pipe(getUpperCaseTransform())
477477
.pipe(fileStream)
478+
const o = await p
478479

479480
assert.ok(p instanceof WriteStream)
480-
assert.equal(await p, fileStream)
481+
assert.ok(o instanceof WriteStream)
482+
assert.equal(o.stdout, 'hello\n')
483+
assert.equal(o.exitCode, 0)
481484
assert.equal((await fs.readFile(file)).toString(), 'HELLO\n')
482485
await fs.rm(file)
483486
})
484487

488+
test('$ > stdout', async () => {
489+
const p = $`echo 1`.pipe(process.stdout)
490+
assert.deepEqual(p, process.stdout)
491+
})
492+
485493
test('$ halted > stream', async () => {
486494
const file = tempfile()
487495
const fileStream = fs.createWriteStream(file)
@@ -514,11 +522,6 @@ describe('core', () => {
514522

515523
assert.equal(stdout, 'HELLO\n')
516524
})
517-
518-
test('$ > stdout', async () => {
519-
const p = $`echo 1`.pipe(process.stdout)
520-
assert.equal(await p, process.stdout)
521-
})
522525
})
523526

524527
it('supports delayed piping', async () => {

0 commit comments

Comments
 (0)