@@ -154,9 +154,6 @@ function unwrapIfNotMulti(paths, idProps, spec, anyVals, depType) {
154
154
']'
155
155
) ;
156
156
}
157
- // TODO: unwrapped list of wildcard ids?
158
- // eslint-disable-next-line no-console
159
- console . log ( paths . objs ) ;
160
157
throw new ReferenceError (
161
158
'A nonexistent object was used in an `' +
162
159
depType +
@@ -247,20 +244,47 @@ async function fireReadyCallbacks(dispatch, getState, callbacks) {
247
244
248
245
let payload ;
249
246
try {
250
- const outputs = allOutputs . map ( ( out , i ) =>
251
- unwrapIfNotMulti (
252
- paths ,
253
- map ( pick ( [ 'id' , 'property' ] ) , out ) ,
254
- cb . callback . outputs [ i ] ,
255
- cb . anyVals ,
256
- 'Output'
257
- )
258
- ) ;
247
+ const inVals = fillVals ( paths , layout , cb , inputs , 'Input' , true ) ;
248
+
249
+ const preventCallback = ( ) => {
250
+ removeCallbackFromPending ( ) ;
251
+ // no server call here; for performance purposes pretend this is
252
+ // a clientside callback and defer fireNext for the end
253
+ // of the currently-ready callbacks.
254
+ hasClientSide = true ;
255
+ return null ;
256
+ } ;
257
+
258
+ if ( inVals === null ) {
259
+ return preventCallback ( ) ;
260
+ }
261
+
262
+ let outputs ;
263
+ try {
264
+ outputs = allOutputs . map ( ( out , i ) =>
265
+ unwrapIfNotMulti (
266
+ paths ,
267
+ map ( pick ( [ 'id' , 'property' ] ) , out ) ,
268
+ cb . callback . outputs [ i ] ,
269
+ cb . anyVals ,
270
+ 'Output'
271
+ )
272
+ ) ;
273
+ } catch ( e ) {
274
+ if ( e instanceof ReferenceError && ! flatten ( inVals ) . length ) {
275
+ // This case is all-empty multivalued wildcard inputs,
276
+ // which we would normally fire the callback for, except
277
+ // some outputs are missing. So instead we treat it like
278
+ // regular missing inputs and just silently prevent it.
279
+ return preventCallback ( ) ;
280
+ }
281
+ throw e ;
282
+ }
259
283
260
284
payload = {
261
285
output,
262
286
outputs : isMultiOutputProp ( output ) ? outputs : outputs [ 0 ] ,
263
- inputs : fillVals ( paths , layout , cb , inputs , 'Input' ) ,
287
+ inputs : inVals ,
264
288
changedPropIds : keys ( cb . changedPropIds ) ,
265
289
} ;
266
290
if ( cb . callback . state . length ) {
@@ -360,14 +384,18 @@ async function fireReadyCallbacks(dispatch, getState, callbacks) {
360
384
updatePending ( pendingCallbacks , without ( updated , allPropIds ) ) ;
361
385
}
362
386
363
- function handleError ( err ) {
387
+ function removeCallbackFromPending ( ) {
364
388
const { pendingCallbacks} = getState ( ) ;
365
389
if ( requestIsActive ( pendingCallbacks , resolvedId , requestId ) ) {
366
390
// Skip all prop updates from this callback, and remove
367
391
// it from the pending list so callbacks it was blocking
368
392
// that have other changed inputs will still fire.
369
393
updatePending ( pendingCallbacks , allPropIds ) ;
370
394
}
395
+ }
396
+
397
+ function handleError ( err ) {
398
+ removeCallbackFromPending ( ) ;
371
399
const outputs = payload
372
400
? map ( combineIdAndProp , flatten ( [ payload . outputs ] ) ) . join ( ', ' )
373
401
: output ;
@@ -398,9 +426,12 @@ async function fireReadyCallbacks(dispatch, getState, callbacks) {
398
426
return hasClientSide ? fireNext ( ) . then ( done ) : done ;
399
427
}
400
428
401
- function fillVals ( paths , layout , cb , specs , depType ) {
429
+ function fillVals ( paths , layout , cb , specs , depType , allowAllMissing ) {
402
430
const getter = depType === 'Input' ? cb . getInputs : cb . getState ;
403
- return getter ( paths ) . map ( ( inputList , i ) =>
431
+ const errors = [ ] ;
432
+ let emptyMultiValues = 0 ;
433
+
434
+ const fillInputs = ( inputList , i ) =>
404
435
unwrapIfNotMulti (
405
436
paths ,
406
437
inputList . map ( ( { id, property, path : path_ } ) => ( {
@@ -411,8 +442,45 @@ function fillVals(paths, layout, cb, specs, depType) {
411
442
specs [ i ] ,
412
443
cb . anyVals ,
413
444
depType
414
- )
415
- ) ;
445
+ ) ;
446
+
447
+ const tryFill = ( inputList , i ) => {
448
+ try {
449
+ const inputs = fillInputs ( inputList , i ) ;
450
+ if ( isMultiValued ( specs [ i ] ) && ! inputs . length ) {
451
+ emptyMultiValues ++ ;
452
+ }
453
+ return inputs ;
454
+ } catch ( e ) {
455
+ if ( e instanceof ReferenceError ) {
456
+ errors . push ( e ) ;
457
+ return null ;
458
+ }
459
+ // any other error we still want to see!
460
+ throw e ;
461
+ }
462
+ } ;
463
+
464
+ const inputVals = getter ( paths ) . map ( allowAllMissing ? tryFill : fillInputs ) ;
465
+
466
+ if ( errors . length ) {
467
+ if ( errors . length + emptyMultiValues === inputVals . length ) {
468
+ // We have at least one non-multivalued input, but all simple and
469
+ // multi-valued inputs are missing.
470
+ // (if all inputs are multivalued and all missing we still return
471
+ // them as normal, and fire the callback.)
472
+ return null ;
473
+ }
474
+ // If we get here we have some missing and some present inputs.
475
+ // That's a real error, so rethrow the first missing error.
476
+ // Wildcard reference errors mention a list of wildcard specs logged
477
+ // TODO: unwrapped list of wildcard ids?
478
+ // eslint-disable-next-line no-console
479
+ console . log ( paths . objs ) ;
480
+ throw errors [ 0 ] ;
481
+ }
482
+
483
+ return inputVals ;
416
484
}
417
485
418
486
function handleServerside ( config , payload , hooks ) {
0 commit comments