1- import { ChildProcessWithoutNullStreams } from "child_process" ;
1+ import * as util from "util" ;
2+ import * as child_process from "child_process" ;
23import { Process , ProcessTree } from "." ;
3- import { Transform } from "stream" ;
4+
5+ const exec = util . promisify ( child_process . execFile ) ;
46
57/** Parses process information from a given line of process output. */
68export type ProcessTreeParser = ( line : string ) => Process | undefined ;
@@ -10,93 +12,30 @@ export type ProcessTreeParser = (line: string) => Process | undefined;
1012 */
1113export abstract class BaseProcessTree implements ProcessTree {
1214 /**
13- * Spawn the process responsible for collecting all processes on the system.
15+ * Get the command responsible for collecting all processes on the system.
1416 */
15- protected abstract spawnProcess ( ) : ChildProcessWithoutNullStreams ;
17+ protected abstract getCommand ( ) : string ;
18+
19+ /**
20+ * Get the list of arguments used to launch the command.
21+ */
22+ protected abstract getCommandArguments ( ) : string [ ] ;
1623
1724 /**
1825 * Create a new parser that can read the process information from stdout of the process
1926 * spawned by {@link spawnProcess spawnProcess()}.
2027 */
2128 protected abstract createParser ( ) : ProcessTreeParser ;
2229
23- listAllProcesses ( ) : Promise < Process [ ] > {
24- return new Promise < Process [ ] > ( ( resolve , reject ) => {
25- const proc = this . spawnProcess ( ) ;
26- const parser = this . createParser ( ) ;
27-
28- // Capture processes from stdout
29- const processes : Process [ ] = [ ] ;
30- proc . stdout . pipe ( new LineBasedStream ( ) ) . on ( "data" , ( line ) => {
31- const process = parser ( line . toString ( ) ) ;
32- if ( process && process . id !== proc . pid ) {
33- processes . push ( process ) ;
34- }
35- } ) ;
36-
37- // Resolve or reject the promise based on exit code/signal/error
38- proc . on ( "error" , reject ) ;
39- proc . on ( "exit" , ( code , signal ) => {
40- if ( code === 0 ) {
41- resolve ( processes ) ;
42- } else if ( signal ) {
43- reject (
44- new Error (
45- `Unable to list processes: process exited due to signal ${ signal } ` ,
46- ) ,
47- ) ;
48- } else {
49- reject (
50- new Error (
51- `Unable to list processes: process exited with code ${ code } ` ,
52- ) ,
53- ) ;
54- }
55- } ) ;
56- } ) ;
57- }
58- }
59-
60- /**
61- * A stream that emits each line as a single chunk of data. The end of a line is denoted
62- * by the newline character '\n'.
63- */
64- export class LineBasedStream extends Transform {
65- private readonly newline : number = "\n" . charCodeAt ( 0 ) ;
66- private buffer : Buffer = Buffer . alloc ( 0 ) ;
67-
68- override _transform (
69- chunk : Buffer ,
70- _encoding : string ,
71- callback : ( ) => void ,
72- ) : void {
73- let currentIndex = 0 ;
74- while ( currentIndex < chunk . length ) {
75- const newlineIndex = chunk . indexOf ( this . newline , currentIndex ) ;
76- if ( newlineIndex === - 1 ) {
77- this . buffer = Buffer . concat ( [
78- this . buffer ,
79- chunk . subarray ( currentIndex ) ,
80- ] ) ;
81- break ;
30+ async listAllProcesses ( ) : Promise < Process [ ] > {
31+ const execCommand = exec ( this . getCommand ( ) , this . getCommandArguments ( ) ) ;
32+ const parser = this . createParser ( ) ;
33+ return ( await execCommand ) . stdout . split ( "\n" ) . flatMap ( ( line ) => {
34+ const process = parser ( line . toString ( ) ) ;
35+ if ( ! process || process . id === execCommand . child . pid ) {
36+ return [ ] ;
8237 }
83-
84- const newlineChunk = chunk . subarray ( currentIndex , newlineIndex ) ;
85- const line = Buffer . concat ( [ this . buffer , newlineChunk ] ) ;
86- this . push ( line ) ;
87- this . buffer = Buffer . alloc ( 0 ) ;
88-
89- currentIndex = newlineIndex + 1 ;
90- }
91-
92- callback ( ) ;
93- }
94-
95- override _flush ( callback : ( ) => void ) : void {
96- if ( this . buffer . length ) {
97- this . push ( this . buffer ) ;
98- }
99-
100- callback ( ) ;
38+ return [ process ] ;
39+ } ) ;
10140 }
10241}
0 commit comments