Skip to content

Commit 1fc9d0b

Browse files
authored
feat: make ProcessOutput iterable (#1101)
* feat: make `ProcessOutput` iterable closes #1053 closes #1027 superseds #1088 relates #984 * refactor: reuse iterators logic
1 parent ea5f5c0 commit 1fc9d0b

File tree

5 files changed

+43
-15
lines changed

5 files changed

+43
-15
lines changed

.size-limit.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"name": "zx/core",
44
"path": ["build/core.cjs", "build/util.cjs", "build/vendor-core.cjs"],
5-
"limit": "77 kB",
5+
"limit": "77.5 kB",
66
"brotli": false,
77
"gzip": false
88
},
@@ -30,7 +30,7 @@
3030
{
3131
"name": "all",
3232
"path": "build/*",
33-
"limit": "849 kB",
33+
"limit": "850 kB",
3434
"brotli": false,
3535
"gzip": false
3636
}

src/core.ts

+16-13
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ import {
4747
log,
4848
isString,
4949
isStringLiteral,
50-
bufToString,
5150
getLast,
51+
getLines,
5252
noop,
5353
once,
5454
parseBool,
@@ -601,26 +601,19 @@ export class ProcessPromise extends Promise<ProcessOutput> {
601601

602602
// Async iterator API
603603
async *[Symbol.asyncIterator](): AsyncIterator<string> {
604-
let last: string | undefined
605-
const getLines = (chunk: Buffer | string) => {
606-
const lines = ((last || '') + bufToString(chunk)).split('\n')
607-
last = lines.pop()
608-
return lines
609-
}
604+
const memo: (string | undefined)[] = []
610605

611606
for (const chunk of this._zurk!.store.stdout) {
612-
const lines = getLines(chunk)
613-
for (const line of lines) yield line
607+
yield* getLines(chunk, memo)
614608
}
615609

616610
for await (const chunk of this.stdout[Symbol.asyncIterator]
617611
? this.stdout
618612
: VoidStream.from(this.stdout)) {
619-
const lines = getLines(chunk)
620-
for (const line of lines) yield line
613+
yield* getLines(chunk, memo)
621614
}
622615

623-
if (last) yield last
616+
if (memo[0]) yield memo[0]
624617

625618
if ((await this.exitCode) !== 0) throw this._output
626619
}
@@ -758,13 +751,23 @@ export class ProcessOutput extends Error {
758751
}
759752

760753
lines(): string[] {
761-
return this.valueOf().split(/\r?\n/)
754+
return [...this]
762755
}
763756

764757
valueOf(): string {
765758
return this.stdall.trim()
766759
}
767760

761+
*[Symbol.iterator](): Iterator<string> {
762+
const memo: (string | undefined)[] = []
763+
764+
for (const chunk of this._dto.store.stdall) {
765+
yield* getLines(chunk, memo)
766+
}
767+
768+
if (memo[0]) yield memo[0]
769+
}
770+
768771
static getExitMessage = formatExitMessage
769772

770773
static getErrorMessage = formatErrorMessage;

src/util.ts

+9
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,12 @@ export const toCamelCase = (str: string) =>
383383

384384
export const parseBool = (v: string): boolean | string =>
385385
({ true: true, false: false })[v] ?? v
386+
387+
export const getLines = (
388+
chunk: Buffer | string,
389+
next: (string | undefined)[]
390+
) => {
391+
const lines = ((next.pop() || '') + bufToString(chunk)).split(/\r?\n/)
392+
next.push(lines.pop())
393+
return lines
394+
}

test/core.test.js

+15
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,21 @@ describe('core', () => {
11621162
globalThis.Blob = Blob
11631163
})
11641164

1165+
test('[Symbol.Iterator]', () => {
1166+
const o = new ProcessOutput({
1167+
store: {
1168+
stdall: ['foo\nba', 'r\nbaz'],
1169+
},
1170+
})
1171+
const lines = []
1172+
const expected = ['foo', 'bar', 'baz']
1173+
for (const line of o) {
1174+
lines.push(line)
1175+
}
1176+
assert.deepEqual(lines, expected)
1177+
assert.deepEqual(o.lines(), expected)
1178+
})
1179+
11651180
describe('static', () => {
11661181
test('getExitMessage()', () => {
11671182
assert.match(

test/smoke/node.test.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'zx/globals'
1919
{
2020
const p = await $`echo foo`
2121
assert.match(p.stdout, /foo/)
22+
assert.deepEqual(p.lines(), ['foo'])
2223
}
2324

2425
// captures err stack

0 commit comments

Comments
 (0)