@@ -54,6 +54,7 @@ import {report} from './ReporterRunner';
54
54
import { PromiseQueue } from '@parcel/utils' ;
55
55
import type { Cache } from '@parcel/cache' ;
56
56
import { getConfigKeyContentHash } from './requests/ConfigRequest' ;
57
+ import { getFeatureFlag } from '@parcel/feature-flags' ;
57
58
58
59
export const requestGraphEdgeTypes = {
59
60
subrequest : 2 ,
@@ -132,6 +133,7 @@ type Request<TInput, TResult> = {|
132
133
id : string ,
133
134
+ type : RequestType ,
134
135
input : TInput ,
136
+ incompleteRequest ?: boolean ,
135
137
run : ( { | input : TInput , ...StaticRunOpts < TResult > | } ) => Async < TResult > ,
136
138
| } ;
137
139
@@ -1029,6 +1031,10 @@ export default class RequestTracker {
1029
1031
options : ParcelOptions ;
1030
1032
signal : ?AbortSignal ;
1031
1033
stats : Map < RequestType , number > = new Map ( ) ;
1034
+ incompleteRequests : Map <
1035
+ Request < mixed , mixed > [ 'id' ] ,
1036
+ { | deferred : Deferred < void > , promise: Promise< void > , timerId: TimeoutID|} ,
1037
+ > = new Map ( ) ;
1032
1038
1033
1039
constructor ( {
1034
1040
graph,
@@ -1162,11 +1168,63 @@ export default class RequestTracker {
1162
1168
this . graph . replaceSubrequests ( requestNodeId , subrequestContextKeys ) ;
1163
1169
}
1164
1170
1171
+ flushCachedRequest ( requestId : ContentKey ) {
1172
+ let existingPromise = this . incompleteRequests . get ( requestId ) ;
1173
+
1174
+ if ( existingPromise ) {
1175
+ logger . verbose ( {
1176
+ origin : '@parcel/core' ,
1177
+ message : 'Incomplete request promise resolved' ,
1178
+ meta : { trackableEvent : 'incomplete_promise_resolved' } ,
1179
+ } ) ;
1180
+
1181
+ clearTimeout ( existingPromise . timerId ) ;
1182
+ existingPromise . deferred . resolve ( ) ;
1183
+ }
1184
+ }
1185
+
1186
+ waitForFullRequest ( requestId : ContentKey ) : Promise < void > {
1187
+ let existingPromise = this . incompleteRequests . get ( requestId ) ;
1188
+
1189
+ if ( existingPromise ) {
1190
+ return existingPromise . promise ;
1191
+ }
1192
+
1193
+ let deferredPromise = makeDeferredWithPromise ( ) ;
1194
+
1195
+ let timerId = setTimeout ( ( ) => {
1196
+ logger . verbose ( {
1197
+ origin : '@parcel/core' ,
1198
+ message : 'Incomplete request promise timed out' ,
1199
+ meta : { trackableEvent : 'incomplete_promise_timeout' } ,
1200
+ } ) ;
1201
+ deferredPromise . deferred . resolve ( ) ;
1202
+ } , 5_000 ) ;
1203
+
1204
+ this . incompleteRequests . set ( requestId , { ...deferredPromise , timerId} ) ;
1205
+
1206
+ return deferredPromise . promise ;
1207
+ }
1208
+
1165
1209
async runRequest < TInput , TResult > (
1166
1210
request : Request < TInput , TResult > ,
1167
1211
opts ?: ?RunRequestOpts ,
1168
1212
) : Promise < TResult > {
1169
- let requestId = this . graph . hasContentKey ( request . id )
1213
+ let hasKey = this . graph . hasContentKey ( request . id ) ;
1214
+
1215
+ if (
1216
+ request . incompleteRequest &&
1217
+ ! hasKey &&
1218
+ getFeatureFlag ( 'devDepRequestBugFix' )
1219
+ ) {
1220
+ // To avoid possible race conditions with the worker farm build cache, we
1221
+ // suspend processing of an incomplete request until the full details are back
1222
+ // from another worker.
1223
+ await this . waitForFullRequest ( request . id ) ;
1224
+ hasKey = this . graph . hasContentKey ( request . id ) ;
1225
+ }
1226
+
1227
+ let requestId = hasKey
1170
1228
? this . graph . getNodeIdByContentKey ( request . id )
1171
1229
: undefined ;
1172
1230
let hasValidResult = requestId != null && this . hasValidResult ( requestId ) ;
@@ -1228,6 +1286,9 @@ export default class RequestTracker {
1228
1286
deferred . resolve ( false ) ;
1229
1287
throw err ;
1230
1288
} finally {
1289
+ if ( getFeatureFlag ( 'devDepRequestBugFix' ) ) {
1290
+ this . flushCachedRequest ( request . id ) ;
1291
+ }
1231
1292
this . graph . replaceSubrequests ( requestNodeId , [ ...subRequestContentKeys ] ) ;
1232
1293
}
1233
1294
}
0 commit comments