@@ -512,6 +512,27 @@ function wakeChunk<T>(
512512 fulfillReference ( listener , value , chunk ) ;
513513 }
514514 }
515+
516+ if ( __DEV__ && chunk . status === INITIALIZED ) {
517+ const resolvedValue = resolveLazy ( value ) ;
518+ if ( isReactElementOrArrayLike ( resolvedValue ) || isLazy ( resolvedValue ) ) {
519+ const debugInfo = chunk . _debugInfo . splice ( 0 ) ;
520+ if ( resolvedValue . _debugInfo ) {
521+ // $FlowFixMe[method-unbinding]
522+ resolvedValue . _debugInfo . push . apply (
523+ resolvedValue . _debugInfo ,
524+ debugInfo ,
525+ ) ;
526+ } else {
527+ Object. defineProperty ( resolvedValue , '_debugInfo' , {
528+ configurable : false ,
529+ enumerable : false ,
530+ writable : true ,
531+ value : debugInfo ,
532+ } ) ;
533+ }
534+ }
535+ }
515536}
516537
517538function rejectChunk(
@@ -959,6 +980,24 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
959980 return ;
960981 }
961982 }
983+
984+ if ( __DEV__ ) {
985+ if ( isReactElementOrArrayLike ( value ) ) {
986+ const debugInfo = chunk . _debugInfo . splice ( 0 ) ;
987+ if ( value . _debugInfo ) {
988+ // $FlowFixMe[method-unbinding]
989+ value. _debugInfo . push . apply ( value . _debugInfo , debugInfo ) ;
990+ } else {
991+ Object . defineProperty ( value , '_debugInfo' , {
992+ configurable : false ,
993+ enumerable : false ,
994+ writable : true ,
995+ value : debugInfo ,
996+ } ) ;
997+ }
998+ }
999+ }
1000+
9621001 const initializedChunk : InitializedChunk < T > = (chunk: any);
9631002 initializedChunk.status = INITIALIZED;
9641003 initializedChunk.value = value;
@@ -1052,11 +1091,7 @@ function getTaskName(type: mixed): string {
10521091 // the client. There should only be one for any given owner chain.
10531092 return '"use client"' ;
10541093 }
1055- if (
1056- typeof type === 'object' &&
1057- type !== null &&
1058- type . $$typeof === REACT_LAZY_TYPE
1059- ) {
1094+ if (isLazy(type)) {
10601095 if ( type . _init === readChunk ) {
10611096 // This is a lazy node created by Flight. It is probably a client reference.
10621097 // We use the "use client" string to indicate that this is the boundary into
@@ -1168,7 +1203,6 @@ function initializeElement(
11681203
11691204function createElement (
11701205 response : Response ,
1171- isRoot : boolean ,
11721206 type : mixed ,
11731207 key : mixed ,
11741208 props : mixed ,
@@ -1277,19 +1311,10 @@ function createElement(
12771311 // a Lazy node referencing this Element to let everything around it proceed.
12781312 const blockedChunk : BlockedChunk < React$Element < any >> =
12791313 createBlockedChunk ( response ) ;
1280- if ( __DEV__ ) {
1281- // If this is the root element, forward the live debug info of the
1282- // initializing chunk to the blocked chunk.
1283- if ( isRoot && initializingChunk !== null ) {
1284- blockedChunk . _debugInfo = initializingChunk . _debugInfo ;
1285- }
1286- }
12871314 handler . value = element ;
12881315 handler . chunk = blockedChunk ;
12891316 const lazyNode = createLazyChunkWrapper ( blockedChunk , validated ) ;
12901317 if ( __DEV__ ) {
1291- // Forward the live debug info of the lazy node to the element.
1292- element. _debugInfo = lazyNode . _debugInfo ;
12931318 // After we have initialized any blocked references, initialize stack etc.
12941319 const init = initializeElement . bind ( null , response , element , lazyNode ) ;
12951320 blockedChunk . then ( init , init ) ;
@@ -1346,11 +1371,7 @@ function fulfillReference(
13461371 const { response , handler , parentObject , key , map , path } = reference;
13471372
13481373 for (let i = 1; i < path . length ; i ++ ) {
1349- while (
1350- typeof value === 'object' &&
1351- value !== null &&
1352- value . $$typeof === REACT_LAZY_TYPE
1353- ) {
1374+ while ( isLazy ( value ) ) {
13541375 // We never expect to see a Lazy node on this path because we encode those as
13551376 // separate models. This must mean that we have inserted an extra lazy node
13561377 // e.g. to replace a blocked element. We must instead look for it inside.
@@ -1422,11 +1443,7 @@ function fulfillReference(
14221443 value = value [ path [ i ] ] ;
14231444 }
14241445
1425- while (
1426- typeof value === 'object ' &&
1427- value !== null &&
1428- value . $$typeof === REACT_LAZY_TYPE
1429- ) {
1446+ while ( isLazy ( value ) ) {
14301447 // If what we're referencing is a Lazy it must be because we inserted one as a virtual node
14311448 // while it was blocked by other data. If it's no longer blocked, we can unwrap it.
14321449 const referencedChunk : SomeChunk < any > = value . _payload ;
@@ -1475,7 +1492,7 @@ function fulfillReference(
14751492 const element : any = handler . value ;
14761493 switch ( key ) {
14771494 case '3' :
1478- transferReferencedDebugInfo ( handler . chunk , fulfilledChunk , mappedValue ) ;
1495+ transferReferencedDebugInfo ( handler . chunk , fulfilledChunk ) ;
14791496 element . props = mappedValue ;
14801497 break ;
14811498 case '4' :
@@ -1491,11 +1508,11 @@ function fulfillReference(
14911508 }
14921509 break;
14931510 default:
1494- transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue );
1511+ transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
14951512 break;
14961513 }
14971514 } else if ( __DEV__ && ! reference . isDebug ) {
1498- transferReferencedDebugInfo ( handler . chunk , fulfilledChunk , mappedValue ) ;
1515+ transferReferencedDebugInfo ( handler . chunk , fulfilledChunk ) ;
14991516 }
15001517
15011518 handler.deps--;
@@ -1817,49 +1834,59 @@ function loadServerReference<A: Iterable<any>, T>(
18171834 return ( null : any ) ;
18181835}
18191836
1837+ function isReactElementOrArrayLike (
1838+ value : any ,
1839+ // eslint-disable-next-line no-undef
1840+ ) : value is { _debugInfo : null | ReactDebugInfo , ...} {
1841+ return (
1842+ typeof value === 'object' &&
1843+ value !== null &&
1844+ ( isArray ( value ) ||
1845+ typeof value [ ASYNC_ITERATOR ] === 'function' ||
1846+ value . $$typeof === REACT_ELEMENT_TYPE )
1847+ ) ;
1848+ }
1849+
1850+ function isLazy(
1851+ value: any,
1852+ // eslint-disable-next-line no-undef
1853+ ): implies value is LazyComponent<
1854+ React$Element < any > ,
1855+ SomeChunk< React$Element < any > > ,
1856+ > {
1857+ return (
1858+ typeof value === 'object' &&
1859+ value !== null &&
1860+ value . $$typeof === REACT_LAZY_TYPE
1861+ ) ;
1862+ }
1863+
1864+ function resolveLazy(value: mixed): mixed {
1865+ while ( isLazy ( value ) ) {
1866+ const payload : SomeChunk < any > = value . _payload ;
1867+ if ( payload . status === INITIALIZED ) {
1868+ value = payload . value ;
1869+ continue ;
1870+ }
1871+ break ;
1872+ }
1873+
1874+ return value;
1875+ }
1876+
18201877function transferReferencedDebugInfo (
18211878 parentChunk : null | SomeChunk < any > ,
18221879 referencedChunk: SomeChunk< any > ,
1823- referencedValue: mixed,
18241880): void {
18251881 if ( __DEV__ ) {
1826- const referencedDebugInfo = referencedChunk . _debugInfo ;
1827- // If we have a direct reference to an object that was rendered by a synchronous
1828- // server component, it might have some debug info about how it was rendered.
1829- // We forward this to the underlying object. This might be a React Element or
1830- // an Array fragment.
1831- // If this was a string / number return value we lose the debug info. We choose
1832- // that tradeoff to allow sync server components to return plain values and not
1833- // use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
1834- if (
1835- typeof referencedValue === 'object' &&
1836- referencedValue !== null &&
1837- ( isArray ( referencedValue ) ||
1838- typeof referencedValue [ ASYNC_ITERATOR ] === 'function' ||
1839- referencedValue . $$typeof === REACT_ELEMENT_TYPE )
1840- ) {
1841- // We should maybe use a unique symbol for arrays but this is a React owned array.
1842- // $FlowFixMe[prop-missing]: This should be added to elements.
1843- const existingDebugInfo : ?ReactDebugInfo =
1844- ( referencedValue . _debugInfo : any ) ;
1845- if ( existingDebugInfo == null ) {
1846- Object . defineProperty ( ( referencedValue : any ) , '_debugInfo' , {
1847- configurable : false ,
1848- enumerable : false ,
1849- writable : true ,
1850- value : referencedDebugInfo . slice ( 0 ) , // Clone so that pushing later isn't going into the original
1851- } ) ;
1852- } else {
1853- // $FlowFixMe[method-unbinding]
1854- existingDebugInfo . push . apply ( existingDebugInfo , referencedDebugInfo ) ;
1855- }
1856- }
1857- // We also add the debug info to the initializing chunk since the resolution of that promise is
1858- // also blocked by the referenced debug info. By adding it to both we can track it even if the array/element
1859- // is extracted, or if the root is rendered as is.
1882+ // We add the debug info to the initializing chunk since the resolution of
1883+ // that promise is also blocked by the referenced debug info. By adding it
1884+ // to both we can track it even if the array/element/lazy is extracted, or
1885+ // if the root is rendered as is.
18601886 if ( parentChunk !== null ) {
1861- const parentDebugInfo = parentChunk . _debugInfo ;
1862- if ( parentDebugInfo !== referencedDebugInfo ) {
1887+ const referencedDebugInfo = referencedChunk . _debugInfo ;
1888+ if ( referencedDebugInfo !== null ) {
1889+ const parentDebugInfo = parentChunk . _debugInfo ;
18631890 for ( let i = 0 ; i < referencedDebugInfo . length ; ++ i ) {
18641891 const debugInfoEntry = referencedDebugInfo [ i ] ;
18651892 if ( debugInfoEntry . name != null ) {
@@ -1903,11 +1930,7 @@ function getOutlinedModel<T>(
19031930 case INITIALIZED :
19041931 let value = chunk . value ;
19051932 for ( let i = 1 ; i < path . length ; i ++ ) {
1906- while (
1907- typeof value === 'object' &&
1908- value !== null &&
1909- value . $$typeof === REACT_LAZY_TYPE
1910- ) {
1933+ while ( isLazy ( value ) ) {
19111934 const referencedChunk : SomeChunk < any > = value._payload;
19121935 switch (referencedChunk.status) {
19131936 case RESOLVED_MODEL :
@@ -1977,11 +2000,7 @@ function getOutlinedModel<T>(
19772000 value = value [ path [ i ] ] ;
19782001 }
19792002
1980- while (
1981- typeof value === 'object ' &&
1982- value !== null &&
1983- value . $$typeof === REACT_LAZY_TYPE
1984- ) {
2003+ while ( isLazy ( value ) ) {
19852004 // If what we're referencing is a Lazy it must be because we inserted one as a virtual node
19862005 // while it was blocked by other data. If it's no longer blocked, we can unwrap it.
19872006 const referencedChunk : SomeChunk < any > = value . _payload ;
@@ -2010,7 +2029,7 @@ function getOutlinedModel<T>(
20102029 // If we're resolving the "owner" or "stack" slot of an Element array, we don't call
20112030 // transferReferencedDebugInfo because this reference is to a debug chunk.
20122031 } else {
2013- transferReferencedDebugInfo ( initializingChunk , chunk , chunkValue ) ;
2032+ transferReferencedDebugInfo ( initializingChunk , chunk ) ;
20142033 }
20152034 return chunkValue;
20162035 case PENDING:
@@ -2468,7 +2487,6 @@ function parseModelString(
24682487function parseModelTuple (
24692488 response : Response ,
24702489 value : { + [ key : string ] : JSONValue } | $ReadOnlyArray < JSONValue > ,
2471- isRoot : boolean ,
24722490) : any {
24732491 const tuple : [ mixed , mixed , mixed , mixed ] = ( value : any ) ;
24742492
@@ -2477,7 +2495,6 @@ function parseModelTuple(
24772495 // Or even change the ReactElement type to be an array.
24782496 return createElement (
24792497 response ,
2480- isRoot ,
24812498 tuple [ 1 ] ,
24822499 tuple [ 2 ] ,
24832500 tuple [ 3 ] ,
@@ -2727,18 +2744,34 @@ function resolveChunkDebugInfo(
27272744 chunk : SomeChunk < any > ,
27282745) : void {
27292746 if ( __DEV__ && enableAsyncDebugInfo ) {
2730- // Push the currently resolving chunk's debug info representing the stream
2731- // on the Promise that was waiting on the stream.
2732- const ioInfo = streamState . _debugInfo ;
2733- const debugChunk = chunk . _debugChunk ;
2734- if ( debugChunk != null ) {
2735- // If there's a debug chunk, then we wait for it to resolve before adding
2736- // the stream info as the last entry.
2737- debugChunk . then ( ( ) => {
2738- chunk . _debugInfo . push ( { awaited : ioInfo } ) ;
2739- } ) ;
2747+ // Add the currently resolving chunk's debug info representing the stream
2748+ // to the Promise that was waiting on the stream, or its underlying value.
2749+ const debugInfoEntry : ReactAsyncInfo = { awaited : streamState . _debugInfo } ;
2750+
2751+ const addDebugInfo = ( ) = > {
2752+ const value = resolveLazy ( chunk . value ) ;
2753+ if ( isReactElementOrArrayLike ( value ) ) {
2754+ const debugInfo : ReactDebugInfo = [ debugInfoEntry ] ;
2755+ if ( value . _debugInfo ) {
2756+ // $FlowFixMe[method-unbinding]
2757+ value . _debugInfo . push . apply ( value . _debugInfo , debugInfo ) ;
2758+ } else {
2759+ Object . defineProperty ( value , '_debugInfo' , {
2760+ configurable : false ,
2761+ enumerable : false ,
2762+ writable : true ,
2763+ value : debugInfo ,
2764+ } ) ;
2765+ }
2766+ } else if ( chunk . _debugInfo !== null ) {
2767+ chunk . _debugInfo . push ( debugInfoEntry ) ;
2768+ }
2769+ } ;
2770+
2771+ if (chunk.status === PENDING || chunk.status === BLOCKED) {
2772+ chunk . then ( addDebugInfo , addDebugInfo ) ;
27402773 } else {
2741- chunk . _debugInfo . push ( { awaited : ioInfo } ) ;
2774+ addDebugInfo ( ) ;
27422775 }
27432776 }
27442777}
@@ -5044,8 +5077,7 @@ function createFromJSONCallback(response: Response) {
50445077 return parseModelString ( response , this , key , value ) ;
50455078 }
50465079 if ( typeof value === 'object' && value !== null ) {
5047- const isRoot = key === '' ;
5048- return parseModelTuple ( response , value , isRoot ) ;
5080+ return parseModelTuple ( response , value ) ;
50495081 }
50505082 return value ;
50515083 } ;
0 commit comments