@@ -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
@@ -1027,6 +1029,10 @@ export default class RequestTracker {
1027
1029
farm : WorkerFarm ;
1028
1030
options : ParcelOptions ;
1029
1031
signal : ?AbortSignal ;
1032
+ incompleteRequests : Map <
1033
+ Request < mixed , mixed > [ 'id' ] ,
1034
+ { | deferred : Deferred < void > , promise: Promise< void > , timerId: TimeoutID|} ,
1035
+ > = new Map ( ) ;
1030
1036
1031
1037
constructor ( {
1032
1038
graph,
@@ -1160,11 +1166,63 @@ export default class RequestTracker {
1160
1166
this . graph . replaceSubrequests ( requestNodeId , subrequestContextKeys ) ;
1161
1167
}
1162
1168
1169
+ flushCachedRequest ( requestId : ContentKey ) {
1170
+ let existingPromise = this . incompleteRequests . get ( requestId ) ;
1171
+
1172
+ if ( existingPromise ) {
1173
+ logger . verbose ( {
1174
+ origin : '@parcel/core' ,
1175
+ message : 'Incomplete request promise resolved' ,
1176
+ meta : { trackableEvent : 'incomplete_promise_resolved' } ,
1177
+ } ) ;
1178
+
1179
+ clearTimeout ( existingPromise . timerId ) ;
1180
+ existingPromise . deferred . resolve ( ) ;
1181
+ }
1182
+ }
1183
+
1184
+ waitForFullRequest ( requestId : ContentKey ) : Promise < void > {
1185
+ let existingPromise = this . incompleteRequests . get ( requestId ) ;
1186
+
1187
+ if ( existingPromise ) {
1188
+ return existingPromise . promise ;
1189
+ }
1190
+
1191
+ let deferredPromise = makeDeferredWithPromise ( ) ;
1192
+
1193
+ let timerId = setTimeout ( ( ) => {
1194
+ logger . verbose ( {
1195
+ origin : '@parcel/core' ,
1196
+ message : 'Incomplete request promise timed out' ,
1197
+ meta : { trackableEvent : 'incomplete_promise_timeout' } ,
1198
+ } ) ;
1199
+ deferredPromise . deferred . resolve ( ) ;
1200
+ } , 5_000 ) ;
1201
+
1202
+ this . incompleteRequests . set ( requestId , { ...deferredPromise , timerId} ) ;
1203
+
1204
+ return deferredPromise . promise ;
1205
+ }
1206
+
1163
1207
async runRequest < TInput , TResult > (
1164
1208
request : Request < TInput , TResult > ,
1165
1209
opts ?: ?RunRequestOpts ,
1166
1210
) : Promise < TResult > {
1167
- let requestId = this . graph . hasContentKey ( request . id )
1211
+ let hasKey = this . graph . hasContentKey ( request . id ) ;
1212
+
1213
+ if (
1214
+ request . incompleteRequest &&
1215
+ ! hasKey &&
1216
+ getFeatureFlag ( 'devDepRequestBugFix' )
1217
+ ) {
1218
+ // To avoid possible race conditions with the worker farm build cache, we
1219
+ // suspend processing of an incomplete request until the full details are back
1220
+ // from another worker.
1221
+ await this . waitForFullRequest ( request . id ) ;
1222
+ hasKey = this . graph . hasContentKey ( request . id ) ;
1223
+ }
1224
+
1225
+ let requestId = hasKey
1168
1226
? this . graph . getNodeIdByContentKey ( request . id )
1169
1227
: undefined ;
1170
1228
let hasValidResult = requestId != null && this . hasValidResult ( requestId ) ;
@@ -1223,6 +1281,9 @@ export default class RequestTracker {
1223
1281
deferred . resolve ( false ) ;
1224
1282
throw err ;
1225
1283
} finally {
1284
+ if ( getFeatureFlag ( 'devDepRequestBugFix' ) ) {
1285
+ this . flushCachedRequest ( request . id ) ;
1286
+ }
1226
1287
this . graph . replaceSubrequests ( requestNodeId , [ ...subRequestContentKeys ] ) ;
1227
1288
}
1228
1289
}
0 commit comments