@@ -255,7 +255,14 @@ function loadServerReference<T>(
255
255
}
256
256
}
257
257
promise.then(
258
- createModelResolver(parentChunk, parentObject, key),
258
+ createModelResolver(
259
+ parentChunk,
260
+ parentObject,
261
+ key,
262
+ false,
263
+ response,
264
+ createModel,
265
+ ),
259
266
createModelReject(parentChunk),
260
267
);
261
268
// We need a placeholder value that will be replaced later.
@@ -334,19 +341,31 @@ function createModelResolver<T>(
334
341
chunk: SomeChunk< T > ,
335
342
parentObject: Object,
336
343
key: string,
344
+ cyclic: boolean,
345
+ response: Response,
346
+ map: (response: Response, model: any) => T ,
337
347
) : ( value : any ) => void {
338
348
let blocked ;
339
349
if ( initializingChunkBlockedModel ) {
340
350
blocked = initializingChunkBlockedModel ;
341
- blocked . deps ++ ;
351
+ if ( ! cyclic ) {
352
+ blocked . deps ++ ;
353
+ }
342
354
} else {
343
355
blocked = initializingChunkBlockedModel = {
344
- deps : 1 ,
345
- value : null ,
356
+ deps : cyclic ? 0 : 1 ,
357
+ value : ( null : any ) ,
346
358
} ;
347
359
}
348
360
return value => {
349
- parentObject [ key ] = value ;
361
+ parentObject [ key ] = map ( response , value ) ;
362
+
363
+ // If this is the root object for a model reference, where `blocked.value`
364
+ // is a stale `null`, the resolved value can be used directly.
365
+ if ( key === '' && blocked . value === null ) {
366
+ blocked . value = parentObject [ key ] ;
367
+ }
368
+
350
369
blocked.deps--;
351
370
if (blocked.deps === 0) {
352
371
if ( chunk . status !== BLOCKED ) {
@@ -367,16 +386,61 @@ function createModelReject<T>(chunk: SomeChunk<T>): (error: mixed) => void {
367
386
return ( error : mixed ) = > triggerErrorOnChunk ( chunk , error ) ;
368
387
}
369
388
370
- function getOutlinedModel(response: Response, id: number): any {
389
+ function getOutlinedModel< T > (
390
+ response: Response,
391
+ id: number,
392
+ parentObject: Object,
393
+ key: string,
394
+ map: (response: Response, model: any) => T ,
395
+ ) : T {
371
396
const chunk = getChunk ( response , id ) ;
372
- if ( chunk . status === RESOLVED_MODEL ) {
373
- initializeModelChunk ( chunk ) ;
397
+ switch ( chunk . status ) {
398
+ case RESOLVED_MODEL :
399
+ initializeModelChunk ( chunk ) ;
400
+ break ;
374
401
}
375
- if ( chunk . status !== INITIALIZED ) {
376
- // We know that this is emitted earlier so otherwise it's an error.
377
- throw chunk . reason ;
402
+ // The status might have changed after initialization.
403
+ switch ( chunk . status ) {
404
+ case INITIALIZED :
405
+ return map ( response , chunk . value ) ;
406
+ case PENDING :
407
+ case BLOCKED :
408
+ const parentChunk = initializingChunk ;
409
+ chunk . then (
410
+ createModelResolver (
411
+ parentChunk ,
412
+ parentObject ,
413
+ key ,
414
+ false ,
415
+ response ,
416
+ map ,
417
+ ) ,
418
+ createModelReject ( parentChunk ) ,
419
+ ) ;
420
+ return ( null : any ) ;
421
+ default :
422
+ throw chunk . reason ;
378
423
}
379
- return chunk . value ;
424
+ }
425
+
426
+ function createMap (
427
+ response : Response ,
428
+ model : Array < [ any , any ] > ,
429
+ ): Map< any , any > {
430
+ return new Map ( model ) ;
431
+ }
432
+
433
+ function createSet(response: Response, model: Array< any > ): Set< any > {
434
+ return new Set ( model ) ;
435
+ }
436
+
437
+ function extractIterator(response: Response, model: Array< any > ): Iterator< any > {
438
+ // $FlowFixMe[incompatible-use]: This uses raw Symbols because we're extracting from a native array.
439
+ return model [ Symbol . iterator ] ( ) ;
440
+ }
441
+
442
+ function createModel(response: Response, model: any): any {
443
+ return model ;
380
444
}
381
445
382
446
function parseTypedArray(
@@ -402,10 +466,17 @@ function parseTypedArray(
402
466
} ) ;
403
467
404
468
// Since loading the buffer is an async operation we'll be blocking the parent
405
- // chunk. TODO: This is not safe if the parent chunk needs a mapper like Map.
469
+ // chunk.
406
470
const parentChunk = initializingChunk ;
407
471
promise . then (
408
- createModelResolver ( parentChunk , parentObject , parentKey ) ,
472
+ createModelResolver (
473
+ parentChunk ,
474
+ parentObject ,
475
+ parentKey ,
476
+ false ,
477
+ response ,
478
+ createModel ,
479
+ ) ,
409
480
createModelReject ( parentChunk ) ,
410
481
) ;
411
482
return null ;
@@ -434,7 +505,7 @@ function parseModelString(
434
505
const id = parseInt ( value . slice ( 2 ) , 16 ) ;
435
506
// TODO: Just encode this in the reference inline instead of as a model.
436
507
const metaData : { id : ServerReferenceId , bound : Thenable < Array < any >> } =
437
- getOutlinedModel ( response , id ) ;
508
+ getOutlinedModel ( response , id , obj , key , createModel ) ;
438
509
return loadServerReference (
439
510
response ,
440
511
metaData . id ,
@@ -451,14 +522,12 @@ function parseModelString(
451
522
case 'Q ': {
452
523
// Map
453
524
const id = parseInt ( value . slice ( 2 ) , 16 ) ;
454
- const data = getOutlinedModel ( response , id ) ;
455
- return new Map ( data ) ;
525
+ return getOutlinedModel ( response , id , obj , key , createMap ) ;
456
526
}
457
527
case 'W ': {
458
528
// Set
459
529
const id = parseInt ( value . slice ( 2 ) , 16 ) ;
460
- const data = getOutlinedModel ( response , id ) ;
461
- return new Set ( data ) ;
530
+ return getOutlinedModel ( response , id , obj , key , createSet ) ;
462
531
}
463
532
case 'K ': {
464
533
// FormData
@@ -480,8 +549,7 @@ function parseModelString(
480
549
case 'i ': {
481
550
// Iterator
482
551
const id = parseInt ( value . slice ( 2 ) , 16 ) ;
483
- const data = getOutlinedModel ( response , id ) ;
484
- return data [ Symbol . iterator ] ( ) ;
552
+ return getOutlinedModel ( response , id , obj , key , extractIterator ) ;
485
553
}
486
554
case 'I' : {
487
555
// $Infinity
@@ -563,27 +631,7 @@ function parseModelString(
563
631
564
632
// We assume that anything else is a reference ID.
565
633
const id = parseInt ( value . slice ( 1 ) , 16 ) ;
566
- const chunk = getChunk ( response , id ) ;
567
- switch ( chunk . status ) {
568
- case RESOLVED_MODEL :
569
- initializeModelChunk ( chunk ) ;
570
- break ;
571
- }
572
- // The status might have changed after initialization.
573
- switch ( chunk . status ) {
574
- case INITIALIZED :
575
- return chunk . value ;
576
- case PENDING :
577
- case BLOCKED :
578
- const parentChunk = initializingChunk ;
579
- chunk . then (
580
- createModelResolver ( parentChunk , obj , key ) ,
581
- createModelReject ( parentChunk ) ,
582
- ) ;
583
- return null ;
584
- default :
585
- throw chunk . reason ;
586
- }
634
+ return getOutlinedModel ( response , id , obj , key , createModel ) ;
587
635
}
588
636
return value;
589
637
}
0 commit comments