@@ -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,14 @@ 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+ } ) ;
498+ // self and source are DEV only properties.
478499 Object . defineProperty ( element , '_self' , {
479500 configurable : false ,
480501 enumerable : false ,
@@ -499,6 +520,12 @@ function createLazyChunkWrapper<T>(
499520 _payload : chunk ,
500521 _init : readChunk ,
501522 } ;
523+ if ( __DEV__ ) {
524+ // Ensure we have a live array to track future debug info.
525+ const chunkDebugInfo : ReactDebugInfo =
526+ chunk . _debugInfo || ( chunk . _debugInfo = [ ] ) ;
527+ lazyType . _debugInfo = chunkDebugInfo ;
528+ }
502529 return lazyType;
503530}
504531
@@ -694,7 +721,33 @@ function parseModelString(
694721 // The status might have changed after initialization.
695722 switch ( chunk . status ) {
696723 case INITIALIZED :
697- return chunk . value ;
724+ const chunkValue = chunk . value ;
725+ if ( __DEV__ && chunk . _debugInfo ) {
726+ // If we have a direct reference to an object that was rendered by a synchronous
727+ // server component, it might have some debug info about how it was rendered.
728+ // We forward this to the underlying object. This might be a React Element or
729+ // an Array fragment.
730+ // If this was a string / number return value we lose the debug info. We choose
731+ // that tradeoff to allow sync server components to return plain values and not
732+ // use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
733+ if (
734+ typeof chunkValue === 'object' &&
735+ chunkValue !== null &&
736+ ( Array . isArray ( chunkValue ) ||
737+ chunkValue . $$typeof === REACT_ELEMENT_TYPE ) &&
738+ ! chunkValue . _debugInfo
739+ ) {
740+ // We should maybe use a unique symbol for arrays but this is a React owned array.
741+ // $FlowFixMe[prop-missing]: This should be added to elements.
742+ Object . defineProperty ( chunkValue , '_debugInfo' , {
743+ configurable : false ,
744+ enumerable : false ,
745+ writable : true ,
746+ value : chunk . _debugInfo ,
747+ } ) ;
748+ }
749+ }
750+ return chunkValue ;
698751 case PENDING :
699752 case BLOCKED :
700753 case CYCLIC :
@@ -971,6 +1024,24 @@ function resolveHint<Code: HintCode>(
9711024 dispatchHint ( code , hintModel ) ;
9721025}
9731026
1027+ function resolveDebugInfo (
1028+ response : Response ,
1029+ id : number ,
1030+ debugInfo : { name : string } ,
1031+ ) : void {
1032+ if ( ! __DEV__ ) {
1033+ // These errors should never make it into a build so we don't need to encode them in codes.json
1034+ // eslint-disable-next-line react-internal/prod-error-codes
1035+ throw new Error (
1036+ 'resolveDebugInfo should never be called in production mode. This is a bug in React.' ,
1037+ ) ;
1038+ }
1039+ const chunk = getChunk ( response , id ) ;
1040+ const chunkDebugInfo : ReactDebugInfo =
1041+ chunk . _debugInfo || ( chunk . _debugInfo = [ ] ) ;
1042+ chunkDebugInfo . push ( debugInfo ) ;
1043+ }
1044+
9741045function mergeBuffer (
9751046 buffer : Array < Uint8Array > ,
9761047 lastChunk : Uint8Array ,
@@ -1064,7 +1135,7 @@ function processFullRow(
10641135 case 70 /* "F" */ :
10651136 resolveTypedArray ( response , id , buffer , chunk , Float32Array , 4 ) ;
10661137 return ;
1067- case 68 /* "D " */ :
1138+ case 100 /* "d " */ :
10681139 resolveTypedArray ( response , id , buffer , chunk , Float64Array , 8 ) ;
10691140 return ;
10701141 case 78 /* "N" */ :
@@ -1114,6 +1185,18 @@ function processFullRow(
11141185 resolveText ( response , id , row ) ;
11151186 return ;
11161187 }
1188+ case 68 /* "D" */ : {
1189+ if ( __DEV__ ) {
1190+ const debugInfo = JSON . parse ( row ) ;
1191+ resolveDebugInfo ( response , id , debugInfo ) ;
1192+ return ;
1193+ }
1194+ throw new Error (
1195+ 'Failed to read a RSC payload created by a development version of React ' +
1196+ 'on the server while using a production version on the client. Always use ' +
1197+ 'matching versions on the server and the client.' ,
1198+ ) ;
1199+ }
11171200 case 80 /* "P" */ : {
11181201 if ( enablePostpone ) {
11191202 if ( __DEV__ ) {
@@ -1177,7 +1260,7 @@ export function processBinaryChunk(
11771260 resolvedRowTag === 76 /* "L" */ ||
11781261 resolvedRowTag === 108 /* "l" */ ||
11791262 resolvedRowTag === 70 /* "F" */ ||
1180- resolvedRowTag === 68 /* "D " */ ||
1263+ resolvedRowTag === 100 /* "d " */ ||
11811264 resolvedRowTag === 78 /* "N" */ ||
11821265 resolvedRowTag === 109 /* "m" */ ||
11831266 resolvedRowTag === 86 ) ) /* "V" */
0 commit comments