Skip to content

Commit

Permalink
refactor: minor code improvements (#998)
Browse files Browse the repository at this point in the history
continues #988 #984
  • Loading branch information
antongolub authored Dec 17, 2024
1 parent d9c4f9a commit 2a3b19d
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 104 deletions.
99 changes: 43 additions & 56 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<PropertyKey, Array<'string' | 'boolean'>> = {
preferLocal: ['string', 'boolean'],
detached: ['boolean'],
verbose: ['boolean'],
quiet: ['boolean'],
timeout: ['string'],
timeoutSignal: ['string'],
prefix: ['string'],
postfix: ['string'],
}

const o = Object.entries(env).reduce<Record<string, string | boolean>>(
(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,
Expand Down Expand Up @@ -587,34 +555,26 @@ export class ProcessPromise extends Promise<ProcessOutput> {

// 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
Expand Down Expand Up @@ -968,3 +928,30 @@ const promisifyStream = <S extends Writable>(
: 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<Options>((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)
}
69 changes: 21 additions & 48 deletions test/core.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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"')
})
})

Expand Down

0 comments on commit 2a3b19d

Please sign in to comment.