@@ -3,122 +3,126 @@ import { ChildProcess, spawn } from "child_process";
3
3
import { log } from "./logging.js" ;
4
4
import type { EnvVars } from "./types.js" ;
5
5
6
- export const processManager = setupProcessManager ( ) ;
7
-
8
6
type SpawnOptions = {
9
7
name : string ;
10
8
cmd : string ;
11
9
args : string [ ] ;
12
10
cwd ?: string ;
13
11
} ;
14
12
15
- function setupProcessManager ( ) {
16
- const children : ChildProcess [ ] = [ ] ;
13
+ class ChildProcessManager {
14
+ private children : ChildProcess [ ] = [ ] ;
15
+
16
+ constructor ( ) {
17
+ process . on ( "SIGINT" , ( ) => this . cleanExit ( "SIGINT" ) ) ;
18
+ process . on ( "SIGTERM" , ( ) => this . cleanExit ( "SIGTERM" ) ) ;
19
+ }
17
20
18
- function addChild ( child : ChildProcess ) {
19
- children . push ( child ) ;
21
+ addChild ( child : ChildProcess ) {
22
+ this . children . push ( child ) ;
20
23
}
21
24
22
- function removeChild ( proc : ChildProcess ) {
23
- const index = children . indexOf ( proc ) ;
25
+ removeChild ( proc : ChildProcess ) {
26
+ const index = this . children . indexOf ( proc ) ;
24
27
if ( index !== - 1 ) {
25
- children . splice ( index , 1 ) ;
28
+ this . children . splice ( index , 1 ) ;
26
29
}
27
30
}
28
31
29
- const cleanExit = ( reason : string ) => {
30
- log ( "shutdown" , "warn " , `Received ${ reason } . Cleaning up...` ) ;
31
- children . forEach ( ( child ) => {
32
+ private cleanExit ( reason : string ) {
33
+ log ( "shutdown" , "info " , `Received ${ reason } . Cleaning up...` ) ;
34
+ this . children . forEach ( ( child ) => {
32
35
if ( ! child . killed ) {
33
36
child . kill ( ) ;
34
37
}
35
38
} ) ;
36
39
process . exit ( ) ;
37
- } ;
38
-
39
- process . on ( "SIGINT" , ( ) => cleanExit ( "SIGINT" ) ) ;
40
- process . on ( "SIGTERM" , ( ) => cleanExit ( "SIGTERM" ) ) ;
41
-
42
- function spawnWithLog ( {
43
- name,
44
- cmd,
45
- args,
46
- cwd,
47
- extraEnv = { } ,
48
- } : SpawnOptions & {
49
- extraEnv ?: EnvVars ;
50
- } ) : Promise < number | null > {
51
- return new Promise ( ( resolve , reject ) => {
52
- const proc = spawn ( cmd , args , {
53
- cwd,
54
- env : { ...process . env , ...extraEnv } ,
55
- stdio : [ "ignore" , "pipe" , "pipe" ] ,
56
- } ) ;
57
- addChild ( proc ) ;
58
-
59
- const handleStream = (
60
- stream : NodeJS . ReadableStream ,
61
- type : "stdout" | "stderr"
62
- ) => {
63
- const rl = readline . createInterface ( {
64
- input : stream ,
65
- crlfDelay : Infinity ,
66
- } ) ;
67
-
68
- rl . on ( "line" , ( line ) => {
69
- log ( name , type === "stderr" ? "error" : "info" , line ) ;
70
- } ) ;
71
- } ;
40
+ }
41
+ }
72
42
73
- handleStream ( proc . stdout , "stdout" ) ;
74
- handleStream ( proc . stderr , "stderr" ) ;
43
+ const childProcessManager = new ChildProcessManager ( ) ;
44
+
45
+ export function spawnWithLog ( {
46
+ name,
47
+ cmd,
48
+ args,
49
+ cwd,
50
+ extraEnv = { } ,
51
+ } : SpawnOptions & {
52
+ extraEnv ?: EnvVars ;
53
+ } ) : Promise < { exitCode : number | null } > {
54
+ return new Promise ( ( resolve , reject ) => {
55
+ const proc = spawn ( cmd , args , {
56
+ cwd,
57
+ env : { ...process . env , ...extraEnv } ,
58
+ stdio : [ "ignore" , "pipe" , "pipe" ] ,
59
+ } ) ;
60
+ childProcessManager . addChild ( proc ) ;
75
61
76
- proc . on ( "error" , ( err ) => {
77
- log ( name , "error" , `Process error: ${ err . message } ` ) ;
78
- reject ( err ) ;
79
- } ) ;
62
+ readStreamLines ( proc . stdout , ( line ) => log ( name , "info" , line ) ) ;
63
+ readStreamLines ( proc . stderr , ( line ) => log ( name , "error" , line ) ) ;
80
64
81
- proc . on ( "close" , ( code ) => {
82
- removeChild ( proc ) ;
83
- if ( code === 0 ) {
84
- log ( name , "success" , "Process completed successfully" ) ;
85
- resolve ( code ) ;
86
- } else {
87
- log ( name , "error" , `Process exited with code ${ code } ` ) ;
88
- reject ( code ) ;
89
- }
90
- } ) ;
65
+ proc . on ( "error" , ( err ) => {
66
+ log ( name , "error" , `Process error: ${ err . message } ` ) ;
67
+ reject ( err ) ;
91
68
} ) ;
92
- }
93
69
94
- function spawnAndCollectStdout ( { cmd, args, cwd } : SpawnOptions ) : Promise < {
95
- exitCode : number | null ;
96
- stdoutData : string ;
97
- stderrData : string ;
98
- } > {
99
- let stdoutData = "" ;
100
- let stderrData = "" ;
101
- return new Promise ( ( resolve , reject ) => {
102
- const proc = spawn ( cmd , args , {
103
- cwd,
104
- } ) ;
105
- addChild ( proc ) ;
106
-
107
- proc . stdout . on ( "data" , ( data ) => ( stdoutData += data ) ) ;
108
- proc . stderr . on ( "data" , ( data ) => ( stderrData += data ) ) ;
109
- proc . on ( "close" , ( exitCode ) => {
110
- removeChild ( proc ) ;
70
+ proc . on ( "close" , ( exitCode ) => {
71
+ childProcessManager . removeChild ( proc ) ;
72
+ if ( exitCode === 0 ) {
73
+ log ( name , "success" , "Process completed successfully" ) ;
111
74
resolve ( {
112
75
exitCode,
113
- stdoutData,
114
- stderrData,
115
76
} ) ;
77
+ } else {
78
+ log ( name , "error" , `Process exited with code ${ exitCode } ` ) ;
79
+ reject ( {
80
+ exitCode,
81
+ } ) ;
82
+ }
83
+ } ) ;
84
+ } ) ;
85
+ }
86
+
87
+ export function spawnAndCollectOutput ( {
88
+ cmd,
89
+ args,
90
+ cwd,
91
+ } : SpawnOptions ) : Promise < {
92
+ exitCode : number | null ;
93
+ stdoutData : string ;
94
+ stderrData : string ;
95
+ } > {
96
+ let stdoutData = "" ;
97
+ let stderrData = "" ;
98
+ return new Promise ( ( resolve ) => {
99
+ const proc = spawn ( cmd , args , {
100
+ cwd,
101
+ } ) ;
102
+ childProcessManager . addChild ( proc ) ;
103
+
104
+ readStreamLines ( proc . stdout , ( line ) => ( stdoutData += line + "\n" ) ) ;
105
+ readStreamLines ( proc . stderr , ( line ) => ( stderrData += line + "\n" ) ) ;
106
+
107
+ proc . on ( "close" , ( exitCode ) => {
108
+ childProcessManager . removeChild ( proc ) ;
109
+ resolve ( {
110
+ exitCode,
111
+ stdoutData,
112
+ stderrData,
116
113
} ) ;
117
114
} ) ;
118
- }
115
+ } ) ;
116
+ }
117
+
118
+ function readStreamLines (
119
+ stream : NodeJS . ReadableStream ,
120
+ callback : ( line : string ) => void
121
+ ) {
122
+ const rl = readline . createInterface ( {
123
+ input : stream ,
124
+ crlfDelay : Infinity ,
125
+ } ) ;
119
126
120
- return {
121
- spawnWithLog,
122
- spawnAndCollectStdout,
123
- } ;
127
+ rl . on ( "line" , callback ) ;
124
128
}
0 commit comments