1
- import { spawn } from 'node:child_process' ;
1
+ import {
2
+ ChildProcess ,
3
+ ChildProcessByStdio ,
4
+ SpawnOptionsWithStdioTuple ,
5
+ StdioPipe ,
6
+ spawn ,
7
+ } from 'node:child_process' ;
8
+ import { Readable , Writable } from 'node:stream' ;
2
9
import { calcDuration } from './reports/utils' ;
3
10
4
11
/**
@@ -77,10 +84,12 @@ export class ProcessError extends Error {
77
84
* args: ['--version']
78
85
*
79
86
*/
80
- export type ProcessConfig = {
87
+ export type ProcessConfig = Omit <
88
+ SpawnOptionsWithStdioTuple < StdioPipe , StdioPipe , StdioPipe > ,
89
+ 'stdio'
90
+ > & {
81
91
command : string ;
82
92
args ?: string [ ] ;
83
- cwd ?: string ;
84
93
observer ?: ProcessObserver ;
85
94
ignoreExitCode ?: boolean ;
86
95
} ;
@@ -99,7 +108,8 @@ export type ProcessConfig = {
99
108
* }
100
109
*/
101
110
export type ProcessObserver = {
102
- onStdout ?: ( stdout : string ) => void ;
111
+ onStdout ?: ( stdout : string , sourceProcess ?: ChildProcess ) => void ;
112
+ onStderr ?: ( stderr : string , sourceProcess ?: ChildProcess ) => void ;
103
113
onError ?: ( error : ProcessError ) => void ;
104
114
onComplete ?: ( ) => void ;
105
115
} ;
@@ -133,33 +143,38 @@ export type ProcessObserver = {
133
143
* @param cfg - see {@link ProcessConfig}
134
144
*/
135
145
export function executeProcess ( cfg : ProcessConfig ) : Promise < ProcessResult > {
136
- const { observer , cwd , command, args, ignoreExitCode = false } = cfg ;
137
- const { onStdout, onError, onComplete } = observer ?? { } ;
146
+ const { command, args, observer , ignoreExitCode = false , ... options } = cfg ;
147
+ const { onStdout, onStderr , onError, onComplete } = observer ?? { } ;
138
148
const date = new Date ( ) . toISOString ( ) ;
139
149
const start = performance . now ( ) ;
140
150
141
151
return new Promise ( ( resolve , reject ) => {
142
152
// shell:true tells Windows to use shell command for spawning a child process
143
- const process = spawn ( command , args , { cwd, shell : true } ) ;
153
+ const spawnedProcess = spawn ( command , args ?? [ ] , {
154
+ shell : true ,
155
+ ...options ,
156
+ } ) as ChildProcessByStdio < Writable , Readable , Readable > ;
157
+
144
158
// eslint-disable-next-line functional/no-let
145
159
let stdout = '' ;
146
160
// eslint-disable-next-line functional/no-let
147
161
let stderr = '' ;
148
162
149
- process . stdout . on ( 'data' , data => {
163
+ spawnedProcess . stdout . on ( 'data' , data => {
150
164
stdout += String ( data ) ;
151
- onStdout ?.( String ( data ) ) ;
165
+ onStdout ?.( String ( data ) , spawnedProcess ) ;
152
166
} ) ;
153
167
154
- process . stderr . on ( 'data' , data => {
168
+ spawnedProcess . stderr . on ( 'data' , data => {
155
169
stderr += String ( data ) ;
170
+ onStderr ?.( String ( data ) , spawnedProcess ) ;
156
171
} ) ;
157
172
158
- process . on ( 'error' , err => {
173
+ spawnedProcess . on ( 'error' , err => {
159
174
stderr += err . toString ( ) ;
160
175
} ) ;
161
176
162
- process . on ( 'close' , code => {
177
+ spawnedProcess . on ( 'close' , code => {
163
178
const timings = { date, duration : calcDuration ( start ) } ;
164
179
if ( code === 0 || ignoreExitCode ) {
165
180
onComplete ?.( ) ;
0 commit comments