@@ -499,10 +499,44 @@ function createErrorChunk<T>(
499
499
return new ReactPromise ( ERRORED , null , error ) ;
500
500
}
501
501
502
+ function moveDebugInfoFromChunkToInnerValue< T > (
503
+ chunk: InitializedChunk< T > ,
504
+ value: T,
505
+ ): void {
506
+ // Remove the debug info from the initialized chunk, and add it to the inner
507
+ // value instead. This can be a React element, an array, or an uninitialized
508
+ // Lazy.
509
+ const resolvedValue = resolveLazy ( value ) ;
510
+ if (
511
+ typeof resolvedValue === 'object' &&
512
+ resolvedValue !== null &&
513
+ ( isArray ( resolvedValue ) ||
514
+ typeof resolvedValue [ ASYNC_ITERATOR ] === 'function' ||
515
+ resolvedValue . $$typeof === REACT_ELEMENT_TYPE ||
516
+ resolvedValue . $$typeof === REACT_LAZY_TYPE )
517
+ ) {
518
+ const debugInfo = chunk . _debugInfo . splice ( 0 ) ;
519
+ if ( isArray ( resolvedValue . _debugInfo ) ) {
520
+ // $FlowFixMe[method-unbinding]
521
+ resolvedValue . _debugInfo . unshift . apply (
522
+ resolvedValue . _debugInfo ,
523
+ debugInfo ,
524
+ ) ;
525
+ } else {
526
+ Object . defineProperty ( ( resolvedValue : any ) , '_debugInfo' , {
527
+ configurable : false ,
528
+ enumerable : false ,
529
+ writable : true ,
530
+ value : debugInfo ,
531
+ } ) ;
532
+ }
533
+ }
534
+ }
535
+
502
536
function wakeChunk < T > (
503
537
listeners: Array< InitializationReference | ( T = > mixed)> ,
504
538
value : T ,
505
- chunk : SomeChunk < T > ,
539
+ chunk : InitializedChunk < T > ,
506
540
): void {
507
541
for ( let i = 0 ; i < listeners . length ; i ++ ) {
508
542
const listener = listeners [ i ] ;
@@ -512,6 +546,10 @@ function wakeChunk<T>(
512
546
fulfillReference ( listener , value , chunk ) ;
513
547
}
514
548
}
549
+
550
+ if ( __DEV__ ) {
551
+ moveDebugInfoFromChunkToInnerValue ( chunk , value ) ;
552
+ }
515
553
}
516
554
517
555
function rejectChunk(
@@ -649,7 +687,6 @@ function triggerErrorOnChunk<T>(
649
687
}
650
688
try {
651
689
initializeDebugChunk ( response , chunk ) ;
652
- chunk . _debugChunk = null ;
653
690
if ( initializingHandler !== null ) {
654
691
if ( initializingHandler . errored ) {
655
692
// Ignore error parsing debug info, we'll report the original error instead.
@@ -932,9 +969,9 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
932
969
}
933
970
934
971
if (__DEV__) {
935
- // Lazily initialize any debug info and block the initializing chunk on any unresolved entries.
972
+ // Initialize any debug info and block the initializing chunk on any
973
+ // unresolved entries.
936
974
initializeDebugChunk ( response , chunk ) ;
937
- chunk . _debugChunk = null ;
938
975
}
939
976
940
977
try {
@@ -946,7 +983,14 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
946
983
if ( resolveListeners !== null ) {
947
984
cyclicChunk . value = null ;
948
985
cyclicChunk . reason = null ;
949
- wakeChunk ( resolveListeners , value , cyclicChunk ) ;
986
+ for ( let i = 0 ; i < resolveListeners . length ; i ++ ) {
987
+ const listener = resolveListeners [ i ] ;
988
+ if ( typeof listener === 'function' ) {
989
+ listener ( value ) ;
990
+ } else {
991
+ fulfillReference ( listener , value , cyclicChunk ) ;
992
+ }
993
+ }
950
994
}
951
995
if ( initializingHandler !== null ) {
952
996
if ( initializingHandler . errored ) {
@@ -963,6 +1007,10 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
963
1007
const initializedChunk : InitializedChunk < T > = (chunk: any);
964
1008
initializedChunk.status = INITIALIZED;
965
1009
initializedChunk.value = value;
1010
+
1011
+ if (__DEV__) {
1012
+ moveDebugInfoFromChunkToInnerValue ( initializedChunk , value ) ;
1013
+ }
966
1014
} catch ( error ) {
967
1015
const erroredChunk : ErroredChunk < T > = ( chunk : any ) ;
968
1016
erroredChunk . status = ERRORED ;
@@ -1079,7 +1127,7 @@ function getTaskName(type: mixed): string {
1079
1127
function initializeElement (
1080
1128
response : Response ,
1081
1129
element : any ,
1082
- lazyType : null | LazyComponent <
1130
+ lazyNode : null | LazyComponent <
1083
1131
React$Element < any > ,
1084
1132
SomeChunk< React$Element < any > > ,
1085
1133
> ,
@@ -1151,15 +1199,33 @@ function initializeElement(
1151
1199
initializeFakeStack ( response , owner ) ;
1152
1200
}
1153
1201
1154
- // In case the JSX runtime has validated the lazy type as a static child, we
1155
- // need to transfer this information to the element.
1156
- if (
1157
- lazyType &&
1158
- lazyType . _store &&
1159
- lazyType . _store . validated &&
1160
- ! element . _store . validated
1161
- ) {
1162
- element . _store . validated = lazyType . _store . validated ;
1202
+ if ( lazyNode !== null ) {
1203
+ // In case the JSX runtime has validated the lazy type as a static child, we
1204
+ // need to transfer this information to the element.
1205
+ if (
1206
+ lazyNode . _store &&
1207
+ lazyNode . _store . validated &&
1208
+ ! element . _store . validated
1209
+ ) {
1210
+ element . _store . validated = lazyNode . _store . validated ;
1211
+ }
1212
+
1213
+ // If the lazy node is initialized, we move its debug info to the inner
1214
+ // value.
1215
+ if ( lazyNode . _payload . status === INITIALIZED && lazyNode . _debugInfo ) {
1216
+ const debugInfo = lazyNode . _debugInfo . splice ( 0 ) ;
1217
+ if ( element . _debugInfo ) {
1218
+ // $FlowFixMe[method-unbinding]
1219
+ element . _debugInfo . unshift . apply ( element . _debugInfo , debugInfo ) ;
1220
+ } else {
1221
+ Object . defineProperty ( element , '_debugInfo' , {
1222
+ configurable : false ,
1223
+ enumerable : false ,
1224
+ writable : true ,
1225
+ value : debugInfo ,
1226
+ } ) ;
1227
+ }
1228
+ }
1163
1229
}
1164
1230
1165
1231
// TODO: We should be freezing the element but currently, we might write into
@@ -1279,13 +1345,13 @@ function createElement(
1279
1345
createBlockedChunk ( response ) ;
1280
1346
handler . value = element ;
1281
1347
handler . chunk = blockedChunk ;
1282
- const lazyType = createLazyChunkWrapper ( blockedChunk , validated ) ;
1348
+ const lazyNode = createLazyChunkWrapper ( blockedChunk , validated ) ;
1283
1349
if ( __DEV__ ) {
1284
1350
// After we have initialized any blocked references, initialize stack etc.
1285
- const init = initializeElement . bind ( null , response , element , lazyType ) ;
1351
+ const init = initializeElement . bind ( null , response , element , lazyNode ) ;
1286
1352
blockedChunk . then ( init , init ) ;
1287
1353
}
1288
- return lazyType ;
1354
+ return lazyNode ;
1289
1355
}
1290
1356
}
1291
1357
if ( __DEV__ ) {
@@ -1466,7 +1532,7 @@ function fulfillReference(
1466
1532
const element : any = handler . value ;
1467
1533
switch ( key ) {
1468
1534
case '3' :
1469
- transferReferencedDebugInfo ( handler . chunk , fulfilledChunk , mappedValue ) ;
1535
+ transferReferencedDebugInfo ( handler . chunk , fulfilledChunk ) ;
1470
1536
element . props = mappedValue ;
1471
1537
break ;
1472
1538
case '4' :
@@ -1482,11 +1548,11 @@ function fulfillReference(
1482
1548
}
1483
1549
break;
1484
1550
default:
1485
- transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue );
1551
+ transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
1486
1552
break;
1487
1553
}
1488
1554
} else if ( __DEV__ && ! reference . isDebug ) {
1489
- transferReferencedDebugInfo ( handler . chunk , fulfilledChunk , mappedValue ) ;
1555
+ transferReferencedDebugInfo ( handler . chunk , fulfilledChunk ) ;
1490
1556
}
1491
1557
1492
1558
handler.deps--;
@@ -1808,47 +1874,34 @@ function loadServerReference<A: Iterable<any>, T>(
1808
1874
return ( null : any ) ;
1809
1875
}
1810
1876
1877
+ function resolveLazy ( value : any ) : mixed {
1878
+ while (
1879
+ typeof value === 'object' &&
1880
+ value !== null &&
1881
+ value . $$typeof === REACT_LAZY_TYPE
1882
+ ) {
1883
+ const payload : SomeChunk < any > = value . _payload ;
1884
+ if ( payload . status === INITIALIZED ) {
1885
+ value = payload . value ;
1886
+ continue ;
1887
+ }
1888
+ break ;
1889
+ }
1890
+
1891
+ return value;
1892
+ }
1893
+
1811
1894
function transferReferencedDebugInfo (
1812
1895
parentChunk : null | SomeChunk < any > ,
1813
1896
referencedChunk: SomeChunk< any > ,
1814
- referencedValue: mixed,
1815
1897
): void {
1816
1898
if ( __DEV__ ) {
1817
- const referencedDebugInfo = referencedChunk . _debugInfo ;
1818
- // If we have a direct reference to an object that was rendered by a synchronous
1819
- // server component, it might have some debug info about how it was rendered.
1820
- // We forward this to the underlying object. This might be a React Element or
1821
- // an Array fragment.
1822
- // If this was a string / number return value we lose the debug info. We choose
1823
- // that tradeoff to allow sync server components to return plain values and not
1824
- // use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
1825
- if (
1826
- typeof referencedValue === 'object' &&
1827
- referencedValue !== null &&
1828
- ( isArray ( referencedValue ) ||
1829
- typeof referencedValue [ ASYNC_ITERATOR ] === 'function' ||
1830
- referencedValue . $$typeof === REACT_ELEMENT_TYPE )
1831
- ) {
1832
- // We should maybe use a unique symbol for arrays but this is a React owned array.
1833
- // $FlowFixMe[prop-missing]: This should be added to elements.
1834
- const existingDebugInfo : ?ReactDebugInfo =
1835
- ( referencedValue . _debugInfo : any ) ;
1836
- if ( existingDebugInfo == null ) {
1837
- Object . defineProperty ( ( referencedValue : any ) , '_debugInfo' , {
1838
- configurable : false ,
1839
- enumerable : false ,
1840
- writable : true ,
1841
- value : referencedDebugInfo . slice ( 0 ) , // Clone so that pushing later isn't going into the original
1842
- } ) ;
1843
- } else {
1844
- // $FlowFixMe[method-unbinding]
1845
- existingDebugInfo . push . apply ( existingDebugInfo , referencedDebugInfo ) ;
1846
- }
1847
- }
1848
- // We also add the debug info to the initializing chunk since the resolution of that promise is
1849
- // also blocked by the referenced debug info. By adding it to both we can track it even if the array/element
1850
- // is extracted, or if the root is rendered as is.
1899
+ // We add the debug info to the initializing chunk since the resolution of
1900
+ // that promise is also blocked by the referenced debug info. By adding it
1901
+ // to both we can track it even if the array/element/lazy is extracted, or
1902
+ // if the root is rendered as is.
1851
1903
if ( parentChunk !== null ) {
1904
+ const referencedDebugInfo = referencedChunk . _debugInfo ;
1852
1905
const parentDebugInfo = parentChunk . _debugInfo ;
1853
1906
for ( let i = 0 ; i < referencedDebugInfo . length ; ++ i ) {
1854
1907
const debugInfoEntry = referencedDebugInfo [ i ] ;
@@ -1999,7 +2052,7 @@ function getOutlinedModel<T>(
1999
2052
// If we're resolving the "owner" or "stack" slot of an Element array, we don't call
2000
2053
// transferReferencedDebugInfo because this reference is to a debug chunk.
2001
2054
} else {
2002
- transferReferencedDebugInfo ( initializingChunk , chunk , chunkValue ) ;
2055
+ transferReferencedDebugInfo ( initializingChunk , chunk ) ;
2003
2056
}
2004
2057
return chunkValue;
2005
2058
case PENDING:
@@ -2709,14 +2762,47 @@ function incrementChunkDebugInfo(
2709
2762
}
2710
2763
}
2711
2764
2765
+ function addDebugInfo ( chunk : SomeChunk < any > , debugInfo : ReactDebugInfo ) : void {
2766
+ const value = resolveLazy ( chunk . value ) ;
2767
+ if (
2768
+ typeof value === 'object' &&
2769
+ value !== null &&
2770
+ ( isArray ( value ) ||
2771
+ typeof value [ ASYNC_ITERATOR ] === 'function' ||
2772
+ value . $$typeof === REACT_ELEMENT_TYPE ||
2773
+ value . $$typeof === REACT_LAZY_TYPE )
2774
+ ) {
2775
+ if ( isArray ( value . _debugInfo ) ) {
2776
+ // $FlowFixMe[method-unbinding]
2777
+ value . _debugInfo . push . apply ( value . _debugInfo , debugInfo ) ;
2778
+ } else {
2779
+ Object . defineProperty ( ( value : any ) , '_debugInfo' , {
2780
+ configurable : false ,
2781
+ enumerable : false ,
2782
+ writable : true ,
2783
+ value : debugInfo ,
2784
+ } ) ;
2785
+ }
2786
+ } else {
2787
+ // $FlowFixMe[method-unbinding]
2788
+ chunk . _debugInfo . push . apply ( chunk . _debugInfo , debugInfo ) ;
2789
+ }
2790
+ }
2791
+
2712
2792
function resolveChunkDebugInfo(
2713
2793
streamState: StreamState,
2714
2794
chunk: SomeChunk< any > ,
2715
2795
): void {
2716
2796
if ( __DEV__ && enableAsyncDebugInfo ) {
2717
- // Push the currently resolving chunk's debug info representing the stream on the Promise
2718
- // that was waiting on the stream.
2719
- chunk . _debugInfo . push ( { awaited : streamState . _debugInfo } ) ;
2797
+ // Add the currently resolving chunk's debug info representing the stream
2798
+ // to the Promise that was waiting on the stream, or its underlying value.
2799
+ const debugInfo : ReactDebugInfo = [ { awaited : streamState . _debugInfo } ] ;
2800
+ if ( chunk . status === PENDING || chunk . status === BLOCKED ) {
2801
+ const boundAddDebugInfo = addDebugInfo . bind ( null , chunk , debugInfo ) ;
2802
+ chunk . then ( boundAddDebugInfo , boundAddDebugInfo ) ;
2803
+ } else {
2804
+ addDebugInfo ( chunk , debugInfo ) ;
2805
+ }
2720
2806
}
2721
2807
}
2722
2808
@@ -2909,7 +2995,8 @@ function resolveStream<T: ReadableStream | $AsyncIterable<any, any, void>>(
2909
2995
const resolveListeners = chunk . value ;
2910
2996
2911
2997
if ( __DEV__ ) {
2912
- // Lazily initialize any debug info and block the initializing chunk on any unresolved entries.
2998
+ // Initialize any debug info and block the initializing chunk on any
2999
+ // unresolved entries.
2913
3000
if ( chunk . _debugChunk != null ) {
2914
3001
const prevHandler = initializingHandler ;
2915
3002
const prevChunk = initializingChunk ;
@@ -2923,7 +3010,6 @@ function resolveStream<T: ReadableStream | $AsyncIterable<any, any, void>>(
2923
3010
}
2924
3011
try {
2925
3012
initializeDebugChunk ( response , chunk ) ;
2926
- chunk . _debugChunk = null ;
2927
3013
if ( initializingHandler !== null ) {
2928
3014
if ( initializingHandler . errored ) {
2929
3015
// Ignore error parsing debug info, we'll report the original error instead.
@@ -2947,7 +3033,7 @@ function resolveStream<T: ReadableStream | $AsyncIterable<any, any, void>>(
2947
3033
resolvedChunk.value = stream;
2948
3034
resolvedChunk.reason = controller;
2949
3035
if (resolveListeners !== null) {
2950
- wakeChunk ( resolveListeners , chunk . value , chunk ) ;
3036
+ wakeChunk ( resolveListeners , chunk . value , ( chunk : any ) ) ;
2951
3037
}
2952
3038
}
2953
3039
0 commit comments