Skip to content

Commit

Permalink
Improve line iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Aug 18, 2024
1 parent 102d1c1 commit 8f33d86
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 24 deletions.
59 changes: 42 additions & 17 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import test from 'ava';
import nanoSpawn from './index.js';

// TODO: replace with Array.fromAsync() after dropping support for Node <22.0.0
const arrayFromAsync = async asyncIterable => {
const chunks = [];
for await (const chunk of asyncIterable) {
chunks.push(chunk);
}

return chunks;
};

test('can be awaited', async t => {
const result = await nanoSpawn('echo', ['🦄']);
// TODO
Expand All @@ -10,36 +20,51 @@ test('can be awaited', async t => {

test('stdout produces correct output', async t => {
const result = nanoSpawn('echo', ['Hello\nWorld']);

const lines = [];
for await (const chunk of result.stdout) {
lines.push(chunk.toString());
}

const lines = await arrayFromAsync(result.stdout);
t.deepEqual(lines, ['Hello', 'World']);
});

test('stderr produces correct output', async t => {
const result = nanoSpawn('ls', ['non-existent-file']);

const lines = [];
for await (const line of result.stderr) {
lines.push(line);
}

const lines = await arrayFromAsync(result.stderr);
t.is(lines.length, 1);
t.regex(lines[0], /No such file/);
});

test('combines stdout and stderr correctly', async t => {
const result = nanoSpawn('bash', ['-c', 'echo "stdout\nstdout2"; echo "stderr\nstderr2" 1>&2']);
const lines = await arrayFromAsync(result);
t.deepEqual(lines, ['stdout', 'stderr', 'stdout2', 'stderr2']);
});

const lines = [];
for await (const line of result) {
lines.push(line);
}
test('stdout handles no newline at the end', async t => {
const result = nanoSpawn('node', ['-e', 'process.stdout.write("Hello\\nWorld")']);
const lines = await arrayFromAsync(result.stdout);
t.deepEqual(lines, ['Hello', 'World']);
});

t.deepEqual(lines, ['stdout', 'stderr', 'stdout2', 'stderr2']);
test('stdout handles newline at the end', async t => {
const result = nanoSpawn('node', ['-e', 'process.stdout.write("Hello\\nWorld\\n")']);
const lines = await arrayFromAsync(result.stdout);
t.deepEqual(lines, ['Hello', 'World']);
});

test('stdout handles 2 newlines at the end', async t => {
const result = nanoSpawn('node', ['-e', 'process.stdout.write("Hello\\nWorld\\n\\n")']);
const lines = await arrayFromAsync(result.stdout);
t.deepEqual(lines, ['Hello', 'World', '']);
});

test('stdout handles Windows newlines', async t => {
const result = nanoSpawn('node', ['-e', 'process.stdout.write("Hello\\r\\nWorld")']);
const lines = await arrayFromAsync(result.stdout);
t.deepEqual(lines, ['Hello', 'World']);
});

test('stdout handles Windows newline at the end', async t => {
const result = nanoSpawn('node', ['-e', 'process.stdout.write("Hello\\r\\nWorld\\r\\n")']);
const lines = await arrayFromAsync(result.stdout);
t.deepEqual(lines, ['Hello', 'World']);
});

test('rejects on error', async t => {
Expand Down
12 changes: 5 additions & 7 deletions utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,13 @@ export async function * combineAsyncIterables(iterable1, iterable2) {
}
}

export async function * lineIterator(iterable) {
export async function * lineIterator(stream) {
stream.setEncoding('utf8');
let buffer = '';
for await (const chunk of iterable) {
buffer += chunk;
const lines = buffer.split('\n');
for await (const chunk of stream) {
const lines = `${buffer}${chunk}`.split(/\r?\n/);
buffer = lines.pop(); // Keep last line in buffer as it may not be complete
for (const line of lines) {
yield line;
}
yield * lines;
}

if (buffer) {
Expand Down

0 comments on commit 8f33d86

Please sign in to comment.