From 327dd7a8c46f872a61f7528a93014067df7b8403 Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Sat, 8 Feb 2025 20:55:25 +0300 Subject: [PATCH] refactor: simplify `ProcessOutput` constructor (#1095) relates #1053 * refactor: simplify `ProcessOutput` constructor * refactor: make private lazy dto factory * chore: update dts tests --- src/core.ts | 158 +++++++++++++++++++++++------------------- test-d/core.test-d.ts | 13 ++-- 2 files changed, 94 insertions(+), 77 deletions(-) diff --git a/src/core.ts b/src/core.ts index b7d67150d9..f21d9534f1 100644 --- a/src/core.ts +++ b/src/core.ts @@ -203,9 +203,7 @@ export const $: Shell & Options = new Proxy( }, } ) -/** - * State machine stages - */ + type ProcessStage = 'initial' | 'halted' | 'running' | 'fulfilled' | 'rejected' type Resolve = (out: ProcessOutput) => void @@ -312,35 +310,23 @@ export class ProcessPromise extends Promise { $.log({ kind: 'stderr', data, verbose: !self.isQuiet(), id }) }, end: (data, c) => { - const { error, status, signal, duration, ctx } = data - const { stdout, stderr, stdall } = ctx.store - const dto: ProcessOutputLazyDto = { - code: () => status, - signal: () => signal, - duration: () => duration, - stdout: once(() => bufArrJoin(stdout)), - stderr: once(() => bufArrJoin(stderr)), - stdall: once(() => bufArrJoin(stdall)), - message: once(() => ProcessOutput.getExitMessage( - status, - signal, - dto.stderr(), - self._from - )), - ...error && { - code: () => null, - signal: () => null, - message: () => ProcessOutput.getErrorMessage(error, self._from) - } - } + const { error, status, signal, duration, ctx: {store} } = data + const { stdout, stderr } = store + const output = self._output = new ProcessOutput({ + code: status, + signal, + error, + duration, + store, + from: self._from, + }) + + $.log({ kind: 'end', signal, exitCode: status, duration, error, verbose: self.isVerbose(), id }) // Ensures EOL if (stdout.length && getLast(getLast(stdout)) !== BR_CC) c.on.stdout!(EOL, c) if (stderr.length && getLast(getLast(stderr)) !== BR_CC) c.on.stderr!(EOL, c) - $.log({ kind: 'end', signal, exitCode: status, duration, error, verbose: self.isVerbose(), id }) - const output = self._output = new ProcessOutput(dto) - if (error || status !== 0 && !self.isNothrow()) { self._stage = 'rejected' self._reject(output) @@ -669,76 +655,72 @@ export class ProcessPromise extends Promise { } } -type GettersRecord> = { [K in keyof T]: () => T[K] } - -type ProcessOutputLazyDto = GettersRecord<{ +type ProcessDto = { code: number | null signal: NodeJS.Signals | null + duration: number + error: any + from: string + store: TSpawnStore +} + +type ProcessOutputDto = ProcessDto & { stdout: string stderr: string stdall: string message: string - duration: number -}> +} export class ProcessOutput extends Error { - private readonly _code: number | null = null - private readonly _signal: NodeJS.Signals | null - private readonly _stdout: string - private readonly _stderr: string - private readonly _combined: string - private readonly _duration: number - - constructor(dto: ProcessOutputLazyDto) + private readonly _dto: ProcessOutputDto + constructor(dto: ProcessDto) constructor( code: number | null, signal: NodeJS.Signals | null, stdout: string, stderr: string, - combined: string, + stdall: string, message: string, duration?: number ) constructor( - code: number | null | ProcessOutputLazyDto, + code: number | null | ProcessDto, signal: NodeJS.Signals | null = null, stdout: string = '', stderr: string = '', - combined: string = '', + stdall: string = '', message: string = '', duration: number = 0 ) { super(message) - this._signal = signal - this._stdout = stdout - this._stderr = stderr - this._combined = combined - this._duration = duration - if (code !== null && typeof code === 'object') { - Object.defineProperties(this, { - _code: { get: code.code }, - _signal: { get: code.signal }, - _duration: { get: code.duration }, - _stdout: { get: code.stdout }, - _stderr: { get: code.stderr }, - _combined: { get: code.stdall }, - message: { get: code.message }, - }) - } else { - this._code = code - } + Reflect.deleteProperty(this, 'message') + this._dto = + code !== null && typeof code === 'object' + ? ProcessOutput.createLazyDto(code) + : { + code, + signal, + duration, + stdout, + stderr, + stdall, + error: null, + from: '', + message, + store: { stdout: [], stderr: [], stdall: [] }, + } } toString(): string { - return this._combined + return this._dto.stdall } json(): T { - return JSON.parse(this._combined) + return JSON.parse(this._dto.stdall) } buffer(): Buffer { - return Buffer.from(this._combined) + return Buffer.from(this._dto.stdall) } blob(type = 'text/plain'): Blob { @@ -760,27 +742,63 @@ export class ProcessOutput extends Error { } valueOf(): string { - return this._combined.trim() + return this._dto.stdall.trim() } get stdout(): string { - return this._stdout + return this._dto.stdout } get stderr(): string { - return this._stderr + return this._dto.stderr } get exitCode(): number | null { - return this._code + return this._dto.code } get signal(): NodeJS.Signals | null { - return this._signal + return this._dto.signal } get duration(): number { - return this._duration + return this._dto.duration + } + + get message(): string { + return this._dto.message + } + + private static createLazyDto({ + code, + signal, + duration, + store: { stdout, stderr, stdall }, + from, + error, + }: ProcessDto): ProcessOutputDto { + const dto = Object.defineProperties( + { + code, + signal, + duration, + }, + { + stdout: { get: once(() => bufArrJoin(stdout)) }, + stderr: { get: once(() => bufArrJoin(stderr)) }, + stdall: { get: once(() => bufArrJoin(stdall)) }, + message: { + get: once(() => + ProcessOutput.getExitMessage(code, signal, dto.stderr, from) + ), + }, + ...(error && { + message: { get: () => ProcessOutput.getErrorMessage(error, from) }, + }), + } + ) as ProcessOutputDto + + return dto } static getExitMessage = formatExitMessage diff --git a/test-d/core.test-d.ts b/test-d/core.test-d.ts index 53567dd1f5..00659d9e79 100644 --- a/test-d/core.test-d.ts +++ b/test-d/core.test-d.ts @@ -46,13 +46,12 @@ expectType(o.exitCode) expectType(o.signal) // prettier-ignore expectType(new ProcessOutput({ - code() { return null }, - signal() { return null }, - stdall() { return '' }, - stderr() { return '' }, - stdout() { return '' }, - duration() { return 0 }, - message() { return '' }, + code: null, + signal: null, + duration: 0, + store: { stdout: [], stderr: [], stdall: [] }, + error: null, + from: '' })) expectType(new ProcessOutput(null, null, '', '', '', '', 1))