From 2a3b19d6572a317446e838cd0bc18a91111811cb Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Tue, 17 Dec 2024 19:00:38 +0300 Subject: [PATCH] refactor: minor code improvements (#998) continues #988 #984 --- src/core.ts | 99 ++++++++++++++++++++--------------------------- test/core.test.js | 69 ++++++++++----------------------- 2 files changed, 64 insertions(+), 104 deletions(-) diff --git a/src/core.ts b/src/core.ts index 68d2e34b7a..840b0110b5 100644 --- a/src/core.ts +++ b/src/core.ts @@ -100,7 +100,7 @@ export interface Options { } // prettier-ignore -export const defaults: Options = getZxDefaults({ +export const defaults: Options = resolveDefaults({ [CWD]: process.cwd(), [SYNC]: false, verbose: false, @@ -122,38 +122,6 @@ export const defaults: Options = getZxDefaults({ timeoutSignal: SIGTERM, }) -export function getZxDefaults( - defs: Options, - prefix: string = 'ZX_', - env = process.env -) { - const types: Record> = { - preferLocal: ['string', 'boolean'], - detached: ['boolean'], - verbose: ['boolean'], - quiet: ['boolean'], - timeout: ['string'], - timeoutSignal: ['string'], - prefix: ['string'], - postfix: ['string'], - } - - const o = Object.entries(env).reduce>( - (m, [k, v]) => { - if (v && k.startsWith(prefix)) { - const _k = snakeToCamel(k.slice(prefix.length)) - const _v = { true: true, false: false }[v.toLowerCase()] ?? v - if (_k in types && types[_k].some((type) => type === typeof _v)) { - m[_k] = _v - } - } - return m - }, - {} - ) - return Object.assign(defs, o) -} - // prettier-ignore export interface Shell< S = false, @@ -587,34 +555,26 @@ export class ProcessPromise extends Promise { // Async iterator API async *[Symbol.asyncIterator]() { - const _store = this._zurk!.store.stdout - let _stream - - if (_store.length) { - _stream = VoidStream.from(_store) - } else { - _stream = this.stdout[Symbol.asyncIterator] - ? this.stdout - : VoidStream.from(this.stdout) + let last: string | undefined + const getLines = (chunk: Buffer | string) => { + const lines = ((last || '') + chunk.toString()).split('\n') + last = lines.pop() + return lines } - let buffer = '' - - for await (const chunk of _stream) { - const chunkStr = chunk.toString() - buffer += chunkStr - - let lines = buffer.split('\n') - buffer = lines.pop() || '' - - for (const line of lines) { - yield line - } + for (const chunk of this._zurk!.store.stdout) { + const lines = getLines(chunk) + for (const line of lines) yield line } - if (buffer.length > 0) { - yield buffer + for await (const chunk of this.stdout[Symbol.asyncIterator] + ? this.stdout + : VoidStream.from(this.stdout)) { + const lines = getLines(chunk) + for (const line of lines) yield line } + + if (last) yield last } // Stream-like API @@ -968,3 +928,30 @@ const promisifyStream = ( : promisifyStream(piped as Writable, from) }, }) + +export function resolveDefaults( + defs: Options, + prefix: string = 'ZX_', + env = process.env +) { + const allowed = new Set([ + 'cwd', + 'preferLocal', + 'detached', + 'verbose', + 'quiet', + 'timeout', + 'timeoutSignal', + 'prefix', + 'postfix', + ]) + + return Object.entries(env).reduce((m, [k, v]) => { + if (v && k.startsWith(prefix)) { + const _k = snakeToCamel(k.slice(prefix.length)) + const _v = { true: true, false: false }[v.toLowerCase()] ?? v + if (allowed.has(_k)) (m as any)[_k] = _v + } + return m + }, defs) +} diff --git a/test/core.test.js b/test/core.test.js index a7ed3b6baa..46b48d99ef 100644 --- a/test/core.test.js +++ b/test/core.test.js @@ -19,44 +19,31 @@ import { basename } from 'node:path' import { WriteStream } from 'node:fs' import { Readable, Transform, Writable } from 'node:stream' import { Socket } from 'node:net' -import { ProcessPromise, ProcessOutput, getZxDefaults } from '../build/index.js' +import { + ProcessPromise, + ProcessOutput, + resolveDefaults, +} from '../build/index.js' import '../build/globals.js' describe('core', () => { - describe('getZxDefaults', () => { - test('verbose rewrite', async () => { - const defaults = getZxDefaults({ verbose: false }, 'ZX_', { + describe('resolveDefaults()', () => { + test('overrides known (allowed) opts', async () => { + const defaults = resolveDefaults({ verbose: false }, 'ZX_', { ZX_VERBOSE: 'true', + ZX_PREFER_LOCAL: '/foo/bar/', }) assert.equal(defaults.verbose, true) + assert.equal(defaults.preferLocal, '/foo/bar/') }) - test('verbose ignore', async () => { - const defaults = getZxDefaults({ verbose: false }, 'ZX_', { - ZX_VERBOSE: 'true123', - }) - assert.equal(defaults.verbose, false) - }) - - test('input ignored', async () => { - const defaults = getZxDefaults({}, 'ZX_', { + test('ignores unknown', async () => { + const defaults = resolveDefaults({}, 'ZX_', { ZX_INPUT: 'input', + ZX_FOO: 'test', }) assert.equal(defaults.input, undefined) - }) - - test('preferLocal rewrite boolean', async () => { - const defaults = getZxDefaults({ preferLocal: false }, 'ZX_', { - ZX_PREFER_LOCAL: 'true', - }) - assert.equal(defaults.preferLocal, true) - }) - - test('preferLocal rewrite string', async () => { - const defaults = getZxDefaults({ preferLocal: false }, 'ZX_', { - ZX_PREFER_LOCAL: 'true123', - }) - assert.equal(defaults.preferLocal, 'true123') + assert.equal(defaults.foo, undefined) }) }) @@ -759,7 +746,6 @@ describe('core', () => { describe('[Symbol.asyncIterator]', () => { it('should iterate over lines from stdout', async () => { const process = $`echo "Line1\nLine2\nLine3"` - const lines = [] for await (const line of process) { lines.push(line) @@ -773,7 +759,6 @@ describe('core', () => { it('should handle partial lines correctly', async () => { const process = $`node -e "process.stdout.write('PartialLine1\\nLine2\\nPartial'); setTimeout(() => process.stdout.write('Line3\\n'), 100)"` - const lines = [] for await (const line of process) { lines.push(line) @@ -795,7 +780,6 @@ describe('core', () => { it('should handle empty stdout', async () => { const process = $`echo -n ""` - const lines = [] for await (const line of process) { lines.push(line) @@ -806,7 +790,6 @@ describe('core', () => { it('should handle single line without trailing newline', async () => { const process = $`echo -n "SingleLine"` - const lines = [] for await (const line of process) { lines.push(line) @@ -821,27 +804,17 @@ describe('core', () => { }) it('should yield all buffered and new chunks when iterated after a delay', async () => { - const process = $`sleep 0.1; echo Chunk1; sleep 0.2; echo Chunk2;` - - const collectedChunks = [] - - await new Promise((resolve) => setTimeout(resolve, 400)) + const process = $`sleep 0.1; echo Chunk1; sleep 0.1; echo Chunk2; sleep 0.2; echo Chunk3; sleep 0.1; echo Chunk4;` + const chunks = [] + await new Promise((resolve) => setTimeout(resolve, 250)) for await (const chunk of process) { - collectedChunks.push(chunk) + chunks.push(chunk) } - assert.equal(collectedChunks.length, 2, 'Should have received 2 chunks') - assert.equal( - collectedChunks[0], - 'Chunk1', - 'First chunk should be "Chunk1"' - ) - assert.equal( - collectedChunks[1], - 'Chunk2', - 'Second chunk should be "Chunk2"' - ) + assert.equal(chunks.length, 4, 'Should get all chunks') + assert.equal(chunks[0], 'Chunk1', 'First chunk should be "Chunk1"') + assert.equal(chunks[3], 'Chunk4', 'Second chunk should be "Chunk4"') }) })