@@ -76,53 +76,63 @@ const RESOLVED_MODULE = 'resolved_module';
7676const INITIALIZED = 'fulfilled' ;
7777const ERRORED = 'rejected' ;
7878
79+ // Dev-only
80+ type ReactDebugInfo = Array < { + name ? : string } > ;
81+
7982type PendingChunk < T > = {
8083 status : 'pending' ,
8184 value : null | Array < ( T ) => mixed > ,
8285 reason : null | Array < ( mixed ) => mixed > ,
8386 _response : Response ,
87+ _debugInfo ?: null | ReactDebugInfo ,
8488 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
8589} ;
8690type BlockedChunk < T > = {
8791 status : 'blocked' ,
8892 value : null | Array < ( T ) => mixed > ,
8993 reason : null | Array < ( mixed ) => mixed > ,
9094 _response : Response ,
95+ _debugInfo ?: null | ReactDebugInfo ,
9196 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
9297} ;
9398type CyclicChunk < T > = {
9499 status : 'cyclic' ,
95100 value : null | Array < ( T ) => mixed > ,
96101 reason : null | Array < ( mixed ) => mixed > ,
97102 _response : Response ,
103+ _debugInfo ?: null | ReactDebugInfo ,
98104 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
99105} ;
100106type ResolvedModelChunk < T > = {
101107 status : 'resolved_model' ,
102108 value : UninitializedModel ,
103109 reason : null ,
104110 _response : Response ,
111+ _debugInfo ?: null | ReactDebugInfo ,
105112 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
106113} ;
107114type ResolvedModuleChunk < T > = {
108115 status : 'resolved_module' ,
109116 value : ClientReference < T > ,
110117 reason : null ,
111118 _response : Response ,
119+ _debugInfo ?: null | ReactDebugInfo ,
112120 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
113121} ;
114122type InitializedChunk < T > = {
115123 status : 'fulfilled' ,
116124 value : T ,
117125 reason : null ,
118126 _response : Response ,
127+ _debugInfo ?: null | ReactDebugInfo ,
119128 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
120129} ;
121130type ErroredChunk < T > = {
122131 status : 'rejected' ,
123132 value : null ,
124133 reason : mixed ,
125134 _response : Response ,
135+ _debugInfo ?: null | ReactDebugInfo ,
126136 then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
127137} ;
128138type SomeChunk < T > =
@@ -140,6 +150,9 @@ function Chunk(status: any, value: any, reason: any, response: Response) {
140150 this . value = value ;
141151 this . reason = reason ;
142152 this . _response = response ;
153+ if ( __DEV__ ) {
154+ this . _debugInfo = null ;
155+ }
143156}
144157// We subclass Promise.prototype so that we get other methods like .catch
145158Chunk . prototype = ( Object . create ( Promise . prototype ) : any ) ;
@@ -475,6 +488,13 @@ function createElement(
475488 writable : true ,
476489 value : true , // This element has already been validated on the server.
477490 } ) ;
491+ // debugInfo contains Server Component debug information.
492+ Object . defineProperty ( element , '_debugInfo' , {
493+ configurable : false ,
494+ enumerable : false ,
495+ writable : true ,
496+ value : null ,
497+ } ) ;
478498 }
479499 return element ;
480500}
@@ -487,6 +507,12 @@ function createLazyChunkWrapper<T>(
487507 _payload : chunk ,
488508 _init : readChunk ,
489509 } ;
510+ if ( __DEV__ ) {
511+ // Ensure we have a live array to track future debug info.
512+ const chunkDebugInfo : ReactDebugInfo =
513+ chunk . _debugInfo || ( chunk . _debugInfo = [ ] ) ;
514+ lazyType . _debugInfo = chunkDebugInfo ;
515+ }
490516 return lazyType;
491517}
492518
@@ -682,7 +708,33 @@ function parseModelString(
682708 // The status might have changed after initialization.
683709 switch ( chunk . status ) {
684710 case INITIALIZED :
685- return chunk . value ;
711+ const chunkValue = chunk . value ;
712+ if ( __DEV__ && chunk . _debugInfo ) {
713+ // If we have a direct reference to an object that was rendered by a synchronous
714+ // server component, it might have some debug info about how it was rendered.
715+ // We forward this to the underlying object. This might be a React Element or
716+ // an Array fragment.
717+ // If this was a string / number return value we lose the debug info. We choose
718+ // that tradeoff to allow sync server components to return plain values and not
719+ // use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
720+ if (
721+ typeof chunkValue === 'object' &&
722+ chunkValue !== null &&
723+ ( Array . isArray ( chunkValue ) ||
724+ chunkValue . $$typeof === REACT_ELEMENT_TYPE ) &&
725+ ! chunkValue . _debugInfo
726+ ) {
727+ // We should maybe use a unique symbol for arrays but this is a React owned array.
728+ // $FlowFixMe[prop-missing]: This should be added to elements.
729+ Object . defineProperty ( chunkValue , '_debugInfo' , {
730+ configurable : false ,
731+ enumerable : false ,
732+ writable : true ,
733+ value : chunk . _debugInfo ,
734+ } ) ;
735+ }
736+ }
737+ return chunkValue ;
686738 case PENDING :
687739 case BLOCKED :
688740 case CYCLIC :
@@ -959,6 +1011,24 @@ function resolveHint<Code: HintCode>(
9591011 dispatchHint ( code , hintModel ) ;
9601012}
9611013
1014+ function resolveDebugInfo (
1015+ response : Response ,
1016+ id : number ,
1017+ debugInfo : { name : string } ,
1018+ ) : void {
1019+ if ( ! __DEV__ ) {
1020+ // These errors should never make it into a build so we don't need to encode them in codes.json
1021+ // eslint-disable-next-line react-internal/prod-error-codes
1022+ throw new Error (
1023+ 'resolveDebugInfo should never be called in production mode. This is a bug in React.' ,
1024+ ) ;
1025+ }
1026+ const chunk = getChunk ( response , id ) ;
1027+ const chunkDebugInfo : ReactDebugInfo =
1028+ chunk . _debugInfo || ( chunk . _debugInfo = [ ] ) ;
1029+ chunkDebugInfo . push ( debugInfo ) ;
1030+ }
1031+
9621032function mergeBuffer (
9631033 buffer : Array < Uint8Array > ,
9641034 lastChunk : Uint8Array ,
@@ -1052,7 +1122,7 @@ function processFullRow(
10521122 case 70 /* "F" */ :
10531123 resolveTypedArray ( response , id , buffer , chunk , Float32Array , 4 ) ;
10541124 return ;
1055- case 68 /* "D " */ :
1125+ case 100 /* "d " */ :
10561126 resolveTypedArray ( response , id , buffer , chunk , Float64Array , 8 ) ;
10571127 return ;
10581128 case 78 /* "N" */ :
@@ -1102,6 +1172,18 @@ function processFullRow(
11021172 resolveText ( response , id , row ) ;
11031173 return ;
11041174 }
1175+ case 68 /* "D" */ : {
1176+ if ( __DEV__ ) {
1177+ const debugInfo = JSON . parse ( row ) ;
1178+ resolveDebugInfo ( response , id , debugInfo ) ;
1179+ return ;
1180+ }
1181+ throw new Error (
1182+ 'Failed to read a RSC payload created by a development version of React ' +
1183+ 'on the server while using a production version on the client. Always use ' +
1184+ 'matching versions on the server and the client.' ,
1185+ ) ;
1186+ }
11051187 case 80 /* "P" */ : {
11061188 if ( enablePostpone ) {
11071189 if ( __DEV__ ) {
@@ -1165,7 +1247,7 @@ export function processBinaryChunk(
11651247 resolvedRowTag === 76 /* "L" */ ||
11661248 resolvedRowTag === 108 /* "l" */ ||
11671249 resolvedRowTag === 70 /* "F" */ ||
1168- resolvedRowTag === 68 /* "D " */ ||
1250+ resolvedRowTag === 100 /* "d " */ ||
11691251 resolvedRowTag === 78 /* "N" */ ||
11701252 resolvedRowTag === 109 /* "m" */ ||
11711253 resolvedRowTag === 86 ) ) /* "V" */
0 commit comments