Files
radicle-explorer/tests/support/process.ts
T

85 lines
2.4 KiB
TypeScript

import type { ExecaChildProcess, Options } from "execa";
import * as Stream from "node:stream";
import { StringDecoder } from "string_decoder";
import { execa } from "execa";
import { logPrefix } from "./logPrefix.js";
// Processes that should be SIGKILLed when the Node process shutsdown.
// We add all proxy and node instances that we spawn to this list.
const processes: ExecaChildProcess[] = [];
process.on("exit", killAllProcesses);
process.on("SIGINT", killAllProcesses);
process.on("SIGTERM", killAllProcesses);
// Kill all processes with SIGKILL
export function killAllProcesses(): void {
for (const process of processes) {
if (process.exitCode === null) {
process.kill("SIGKILL");
}
}
}
// Spawn a process with `execa` and register it.
//
// The process will be killed by `killAllProcesses`.
export function spawn(
bin: string,
args: string[],
options?: Options,
): ExecaChildProcess {
const child = execa(bin, args, options);
processes.push(child);
return child;
}
// Forwards piped `stdout` and `stderr` of a child process to `output`
// and prefixes it with the given label. The prefix is colored.
export function prefixOutput(
childProcess: ExecaChildProcess,
label: string,
output: Stream.Writable,
): ExecaChildProcess {
const pref = logPrefix(label);
if (childProcess.stdout) {
const stdoutPrefix = new LinePrefix(pref);
childProcess.stdout.pipe(stdoutPrefix).pipe(output, { end: false });
}
if (childProcess.stderr) {
const stderrPrefix = new LinePrefix(pref);
childProcess.stderr.pipe(stderrPrefix).pipe(output, { end: false });
}
return childProcess;
}
// A transform that prefixes each line from the source with the given
// string and pushes it to the sink.
class LinePrefix extends Stream.Transform {
private buffer: string = "";
private stringDecoder = new StringDecoder();
public constructor(private prefix: string) {
super();
}
public _transform(data: Buffer, _encoding: string, next: () => void): void {
const str = this.buffer + this.stringDecoder.write(data);
const lines = str.split(/\r?\n/);
this.buffer = lines.pop() || "";
lines.forEach(line => this.push(`${this.prefix}${line}\n`));
next();
}
public _flush(done: () => void): void {
const rest = `${this.buffer}${this.stringDecoder.end()}`;
if (rest) {
this.push(`${this.prefix}${rest}\n`);
}
done();
}
}