@@ -70,6 +70,26 @@ function normaliseCommandAndArgs(
70
70
} ;
71
71
}
72
72
73
+ function combineSignals ( signals : Iterable < AbortSignal > ) : AbortSignal {
74
+ const controller = new AbortController ( ) ;
75
+
76
+ for ( const signal of signals ) {
77
+ if ( signal . aborted ) {
78
+ controller . abort ( ) ;
79
+ return signal ;
80
+ }
81
+
82
+ const onAbort = ( ) : void => {
83
+ controller . abort ( signal . reason ) ;
84
+ } ;
85
+ signal . addEventListener ( 'abort' , onAbort , {
86
+ signal : controller . signal
87
+ } ) ;
88
+ }
89
+
90
+ return controller . signal ;
91
+ }
92
+
73
93
export class ExecProcess implements Result {
74
94
protected _process ?: ChildProcess ;
75
95
protected _aborted : boolean = false ;
@@ -78,6 +98,7 @@ export class ExecProcess implements Result {
78
98
protected _args : string [ ] ;
79
99
protected _resolveClose ?: ( ) => void ;
80
100
protected _processClosed : Promise < void > ;
101
+ protected _thrownError ?: Error ;
81
102
82
103
public get process ( ) : ChildProcess | undefined {
83
104
return this . _process ;
@@ -139,6 +160,10 @@ export class ExecProcess implements Result {
139
160
return ;
140
161
}
141
162
163
+ if ( this . _thrownError ) {
164
+ throw this . _thrownError ;
165
+ }
166
+
142
167
const sources : Readable [ ] = [ ] ;
143
168
if ( proc . stderr ) {
144
169
sources . push ( proc . stderr ) ;
@@ -158,10 +183,7 @@ export class ExecProcess implements Result {
158
183
await this . _processClosed ;
159
184
}
160
185
161
- public async then < TResult1 = Output , TResult2 = never > (
162
- onfulfilled ?: ( ( value : Output ) => TResult1 | PromiseLike < TResult1 > ) | null ,
163
- _onrejected ?: ( ( reason : unknown ) => TResult2 | PromiseLike < TResult2 > ) | null
164
- ) : Promise < TResult1 | TResult2 > {
186
+ protected async _waitForOutput ( ) : Promise < Output > {
165
187
if ( this . _options ?. stdin ) {
166
188
await this . _options . stdin ;
167
189
}
@@ -172,22 +194,32 @@ export class ExecProcess implements Result {
172
194
throw new Error ( 'No process was started' ) ;
173
195
}
174
196
197
+ await this . _processClosed ;
198
+
199
+ proc . removeAllListeners ( ) ;
200
+
201
+ if ( this . _thrownError ) {
202
+ throw this . _thrownError ;
203
+ }
204
+
175
205
const [ stderr , stdout ] = await Promise . all ( [
176
206
proc . stderr && readStreamAsString ( proc . stderr ) ,
177
- proc . stdout && readStreamAsString ( proc . stdout ) ,
178
- this . _processClosed
207
+ proc . stdout && readStreamAsString ( proc . stdout )
179
208
] ) ;
180
209
181
210
const result : Output = {
182
211
stderr : stderr ?? '' ,
183
212
stdout : stdout ?? ''
184
213
} ;
185
214
186
- if ( onfulfilled ) {
187
- return onfulfilled ( result ) ;
188
- } else {
189
- return result as TResult1 ;
190
- }
215
+ return result ;
216
+ }
217
+
218
+ public then < TResult1 = Output , TResult2 = never > (
219
+ onfulfilled ?: ( ( value : Output ) => TResult1 | PromiseLike < TResult1 > ) | null ,
220
+ onrejected ?: ( ( reason : unknown ) => TResult2 | PromiseLike < TResult2 > ) | null
221
+ ) : Promise < TResult1 | TResult2 > {
222
+ return this . _waitForOutput ( ) . then ( onfulfilled , onrejected ) ;
191
223
}
192
224
193
225
public spawn ( ) : void {
@@ -198,18 +230,24 @@ export class ExecProcess implements Result {
198
230
...options . nodeOptions
199
231
} ;
200
232
233
+ const signals : AbortSignal [ ] = [ ] ;
234
+
201
235
if ( options . timeout !== undefined ) {
202
- nodeOptions . timeout = options . timeout ;
236
+ signals . push ( AbortSignal . timeout ( options . timeout ) ) ;
203
237
}
204
238
205
239
if ( options . signal !== undefined ) {
206
- nodeOptions . signal = options . signal ;
240
+ signals . push ( options . signal ) ;
207
241
}
208
242
209
243
if ( options . persist === true ) {
210
244
nodeOptions . detached = true ;
211
245
}
212
246
247
+ if ( signals . length > 0 ) {
248
+ nodeOptions . signal = combineSignals ( signals ) ;
249
+ }
250
+
213
251
nodeOptions . env = computeEnv ( cwd , nodeOptions . env ) ;
214
252
215
253
const { command : normalisedCommand , args : normalisedArgs } =
@@ -230,12 +268,14 @@ export class ExecProcess implements Result {
230
268
}
231
269
232
270
protected _onError = ( err : Error ) : void => {
233
- if ( err . name === 'AbortError' ) {
271
+ if (
272
+ err . name === 'AbortError' &&
273
+ ( ! ( err . cause instanceof Error ) || err . cause . name !== 'TimeoutError' )
274
+ ) {
234
275
this . _aborted = true ;
235
276
return ;
236
277
}
237
- // TODO emit this somewhere
238
- throw err ;
278
+ this . _thrownError = err ;
239
279
} ;
240
280
241
281
protected _onClose = ( ) : void => {
0 commit comments