Skip to content

Commit 2a3b19d

Browse files
authored
refactor: minor code improvements (google#998)
continues google#988 google#984
1 parent d9c4f9a commit 2a3b19d

File tree

2 files changed

+64
-104
lines changed

2 files changed

+64
-104
lines changed

src/core.ts

+43-56
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export interface Options {
100100
}
101101

102102
// prettier-ignore
103-
export const defaults: Options = getZxDefaults({
103+
export const defaults: Options = resolveDefaults({
104104
[CWD]: process.cwd(),
105105
[SYNC]: false,
106106
verbose: false,
@@ -122,38 +122,6 @@ export const defaults: Options = getZxDefaults({
122122
timeoutSignal: SIGTERM,
123123
})
124124

125-
export function getZxDefaults(
126-
defs: Options,
127-
prefix: string = 'ZX_',
128-
env = process.env
129-
) {
130-
const types: Record<PropertyKey, Array<'string' | 'boolean'>> = {
131-
preferLocal: ['string', 'boolean'],
132-
detached: ['boolean'],
133-
verbose: ['boolean'],
134-
quiet: ['boolean'],
135-
timeout: ['string'],
136-
timeoutSignal: ['string'],
137-
prefix: ['string'],
138-
postfix: ['string'],
139-
}
140-
141-
const o = Object.entries(env).reduce<Record<string, string | boolean>>(
142-
(m, [k, v]) => {
143-
if (v && k.startsWith(prefix)) {
144-
const _k = snakeToCamel(k.slice(prefix.length))
145-
const _v = { true: true, false: false }[v.toLowerCase()] ?? v
146-
if (_k in types && types[_k].some((type) => type === typeof _v)) {
147-
m[_k] = _v
148-
}
149-
}
150-
return m
151-
},
152-
{}
153-
)
154-
return Object.assign(defs, o)
155-
}
156-
157125
// prettier-ignore
158126
export interface Shell<
159127
S = false,
@@ -587,34 +555,26 @@ export class ProcessPromise extends Promise<ProcessOutput> {
587555

588556
// Async iterator API
589557
async *[Symbol.asyncIterator]() {
590-
const _store = this._zurk!.store.stdout
591-
let _stream
592-
593-
if (_store.length) {
594-
_stream = VoidStream.from(_store)
595-
} else {
596-
_stream = this.stdout[Symbol.asyncIterator]
597-
? this.stdout
598-
: VoidStream.from(this.stdout)
558+
let last: string | undefined
559+
const getLines = (chunk: Buffer | string) => {
560+
const lines = ((last || '') + chunk.toString()).split('\n')
561+
last = lines.pop()
562+
return lines
599563
}
600564

601-
let buffer = ''
602-
603-
for await (const chunk of _stream) {
604-
const chunkStr = chunk.toString()
605-
buffer += chunkStr
606-
607-
let lines = buffer.split('\n')
608-
buffer = lines.pop() || ''
609-
610-
for (const line of lines) {
611-
yield line
612-
}
565+
for (const chunk of this._zurk!.store.stdout) {
566+
const lines = getLines(chunk)
567+
for (const line of lines) yield line
613568
}
614569

615-
if (buffer.length > 0) {
616-
yield buffer
570+
for await (const chunk of this.stdout[Symbol.asyncIterator]
571+
? this.stdout
572+
: VoidStream.from(this.stdout)) {
573+
const lines = getLines(chunk)
574+
for (const line of lines) yield line
617575
}
576+
577+
if (last) yield last
618578
}
619579

620580
// Stream-like API
@@ -968,3 +928,30 @@ const promisifyStream = <S extends Writable>(
968928
: promisifyStream(piped as Writable, from)
969929
},
970930
})
931+
932+
export function resolveDefaults(
933+
defs: Options,
934+
prefix: string = 'ZX_',
935+
env = process.env
936+
) {
937+
const allowed = new Set([
938+
'cwd',
939+
'preferLocal',
940+
'detached',
941+
'verbose',
942+
'quiet',
943+
'timeout',
944+
'timeoutSignal',
945+
'prefix',
946+
'postfix',
947+
])
948+
949+
return Object.entries(env).reduce<Options>((m, [k, v]) => {
950+
if (v && k.startsWith(prefix)) {
951+
const _k = snakeToCamel(k.slice(prefix.length))
952+
const _v = { true: true, false: false }[v.toLowerCase()] ?? v
953+
if (allowed.has(_k)) (m as any)[_k] = _v
954+
}
955+
return m
956+
}, defs)
957+
}

test/core.test.js

+21-48
Original file line numberDiff line numberDiff line change
@@ -19,44 +19,31 @@ import { basename } from 'node:path'
1919
import { WriteStream } from 'node:fs'
2020
import { Readable, Transform, Writable } from 'node:stream'
2121
import { Socket } from 'node:net'
22-
import { ProcessPromise, ProcessOutput, getZxDefaults } from '../build/index.js'
22+
import {
23+
ProcessPromise,
24+
ProcessOutput,
25+
resolveDefaults,
26+
} from '../build/index.js'
2327
import '../build/globals.js'
2428

2529
describe('core', () => {
26-
describe('getZxDefaults', () => {
27-
test('verbose rewrite', async () => {
28-
const defaults = getZxDefaults({ verbose: false }, 'ZX_', {
30+
describe('resolveDefaults()', () => {
31+
test('overrides known (allowed) opts', async () => {
32+
const defaults = resolveDefaults({ verbose: false }, 'ZX_', {
2933
ZX_VERBOSE: 'true',
34+
ZX_PREFER_LOCAL: '/foo/bar/',
3035
})
3136
assert.equal(defaults.verbose, true)
37+
assert.equal(defaults.preferLocal, '/foo/bar/')
3238
})
3339

34-
test('verbose ignore', async () => {
35-
const defaults = getZxDefaults({ verbose: false }, 'ZX_', {
36-
ZX_VERBOSE: 'true123',
37-
})
38-
assert.equal(defaults.verbose, false)
39-
})
40-
41-
test('input ignored', async () => {
42-
const defaults = getZxDefaults({}, 'ZX_', {
40+
test('ignores unknown', async () => {
41+
const defaults = resolveDefaults({}, 'ZX_', {
4342
ZX_INPUT: 'input',
43+
ZX_FOO: 'test',
4444
})
4545
assert.equal(defaults.input, undefined)
46-
})
47-
48-
test('preferLocal rewrite boolean', async () => {
49-
const defaults = getZxDefaults({ preferLocal: false }, 'ZX_', {
50-
ZX_PREFER_LOCAL: 'true',
51-
})
52-
assert.equal(defaults.preferLocal, true)
53-
})
54-
55-
test('preferLocal rewrite string', async () => {
56-
const defaults = getZxDefaults({ preferLocal: false }, 'ZX_', {
57-
ZX_PREFER_LOCAL: 'true123',
58-
})
59-
assert.equal(defaults.preferLocal, 'true123')
46+
assert.equal(defaults.foo, undefined)
6047
})
6148
})
6249

@@ -759,7 +746,6 @@ describe('core', () => {
759746
describe('[Symbol.asyncIterator]', () => {
760747
it('should iterate over lines from stdout', async () => {
761748
const process = $`echo "Line1\nLine2\nLine3"`
762-
763749
const lines = []
764750
for await (const line of process) {
765751
lines.push(line)
@@ -773,7 +759,6 @@ describe('core', () => {
773759

774760
it('should handle partial lines correctly', async () => {
775761
const process = $`node -e "process.stdout.write('PartialLine1\\nLine2\\nPartial'); setTimeout(() => process.stdout.write('Line3\\n'), 100)"`
776-
777762
const lines = []
778763
for await (const line of process) {
779764
lines.push(line)
@@ -795,7 +780,6 @@ describe('core', () => {
795780

796781
it('should handle empty stdout', async () => {
797782
const process = $`echo -n ""`
798-
799783
const lines = []
800784
for await (const line of process) {
801785
lines.push(line)
@@ -806,7 +790,6 @@ describe('core', () => {
806790

807791
it('should handle single line without trailing newline', async () => {
808792
const process = $`echo -n "SingleLine"`
809-
810793
const lines = []
811794
for await (const line of process) {
812795
lines.push(line)
@@ -821,27 +804,17 @@ describe('core', () => {
821804
})
822805

823806
it('should yield all buffered and new chunks when iterated after a delay', async () => {
824-
const process = $`sleep 0.1; echo Chunk1; sleep 0.2; echo Chunk2;`
825-
826-
const collectedChunks = []
827-
828-
await new Promise((resolve) => setTimeout(resolve, 400))
807+
const process = $`sleep 0.1; echo Chunk1; sleep 0.1; echo Chunk2; sleep 0.2; echo Chunk3; sleep 0.1; echo Chunk4;`
808+
const chunks = []
829809

810+
await new Promise((resolve) => setTimeout(resolve, 250))
830811
for await (const chunk of process) {
831-
collectedChunks.push(chunk)
812+
chunks.push(chunk)
832813
}
833814

834-
assert.equal(collectedChunks.length, 2, 'Should have received 2 chunks')
835-
assert.equal(
836-
collectedChunks[0],
837-
'Chunk1',
838-
'First chunk should be "Chunk1"'
839-
)
840-
assert.equal(
841-
collectedChunks[1],
842-
'Chunk2',
843-
'Second chunk should be "Chunk2"'
844-
)
815+
assert.equal(chunks.length, 4, 'Should get all chunks')
816+
assert.equal(chunks[0], 'Chunk1', 'First chunk should be "Chunk1"')
817+
assert.equal(chunks[3], 'Chunk4', 'Second chunk should be "Chunk4"')
845818
})
846819
})
847820

0 commit comments

Comments
 (0)