blob: e7d19f19766db1f4df5344cde43cea288d709cf7 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { ChildProcessWithoutNullStreams } from 'child_process';
// @ts-ignore: no @types/jsonparse available.
import JsonParser from 'jsonparse';
/**
* A stream of Data coming from a process.
*/
export class DataStreamProcess {
constructor(
private process: ChildProcessWithoutNullStreams,
onData: (data: Buffer) => void,
onError: (data: Buffer) => void,
) {
this.process.stdout.on('data', onData);
this.process.stderr.on('data', onError);
}
/**
* A future that resolves when the process exits.
*/
public get exitCode(): Promise<number | null> {
return new Promise(resolve => {
this.process.on('exit', resolve);
});
}
/**
* Kills the underlying process and all its children.
*/
public stop() {
const pid = this.process?.pid;
if (pid) {
try {
// This handles the in-tree use case where `ffx` launches a subprocess `fx ffx`.
process.kill(-pid);
} catch {
// If the process with the given `pid` didn't exist, this error will rise, which we can
// just swallow.
}
}
}
}
/**
* A stream of JSON Data coming from a process.
*/
export class JsonStreamProcess {
private process: DataStreamProcess | undefined;
private parser: JsonParser;
constructor(
process: ChildProcessWithoutNullStreams | undefined,
private onData: (data: Object) => void,
onError: (data: Buffer) => void,
) {
this.parser = new JsonParser();
if (process) {
this.process = new DataStreamProcess(process, (chunk) => {
this.parser.write(chunk);
}, onError);
}
let self = this;
this.parser.onValue = function (value: Object) {
if (this.stack.length === 0) {
self.onData(value);
}
};
}
/**
* Kills the underlying process and all its children.
*/
public stop() {
this.process?.stop();
}
}