@@ -76,53 +76,63 @@ const RESOLVED_MODULE = 'resolved_module';
76
76
const INITIALIZED = 'fulfilled' ;
77
77
const ERRORED = 'rejected' ;
78
78
79
+ // Dev-only
80
+ type ReactDebugInfo = Array < { + name ? : string } > ;
81
+
79
82
type PendingChunk < T > = {
80
83
status : 'pending' ,
81
84
value : null | Array < ( T ) => mixed > ,
82
85
reason : null | Array < ( mixed ) => mixed > ,
83
86
_response : Response ,
87
+ _debugInfo ?: null | ReactDebugInfo ,
84
88
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
85
89
} ;
86
90
type BlockedChunk < T > = {
87
91
status : 'blocked' ,
88
92
value : null | Array < ( T ) => mixed > ,
89
93
reason : null | Array < ( mixed ) => mixed > ,
90
94
_response : Response ,
95
+ _debugInfo ?: null | ReactDebugInfo ,
91
96
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
92
97
} ;
93
98
type CyclicChunk < T > = {
94
99
status : 'cyclic' ,
95
100
value : null | Array < ( T ) => mixed > ,
96
101
reason : null | Array < ( mixed ) => mixed > ,
97
102
_response : Response ,
103
+ _debugInfo ?: null | ReactDebugInfo ,
98
104
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
99
105
} ;
100
106
type ResolvedModelChunk < T > = {
101
107
status : 'resolved_model' ,
102
108
value : UninitializedModel ,
103
109
reason : null ,
104
110
_response : Response ,
111
+ _debugInfo ?: null | ReactDebugInfo ,
105
112
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
106
113
} ;
107
114
type ResolvedModuleChunk < T > = {
108
115
status : 'resolved_module' ,
109
116
value : ClientReference < T > ,
110
117
reason : null ,
111
118
_response : Response ,
119
+ _debugInfo ?: null | ReactDebugInfo ,
112
120
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
113
121
} ;
114
122
type InitializedChunk < T > = {
115
123
status : 'fulfilled' ,
116
124
value : T ,
117
125
reason : null ,
118
126
_response : Response ,
127
+ _debugInfo ?: null | ReactDebugInfo ,
119
128
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
120
129
} ;
121
130
type ErroredChunk < T > = {
122
131
status : 'rejected' ,
123
132
value : null ,
124
133
reason : mixed ,
125
134
_response : Response ,
135
+ _debugInfo ?: null | ReactDebugInfo ,
126
136
then ( resolve : ( T ) => mixed , reject : ( mixed ) => mixed ) : void ,
127
137
} ;
128
138
type SomeChunk < T > =
@@ -140,6 +150,9 @@ function Chunk(status: any, value: any, reason: any, response: Response) {
140
150
this . value = value ;
141
151
this . reason = reason ;
142
152
this . _response = response ;
153
+ if ( __DEV__ ) {
154
+ this . _debugInfo = null ;
155
+ }
143
156
}
144
157
// We subclass Promise.prototype so that we get other methods like .catch
145
158
Chunk . prototype = ( Object . create ( Promise . prototype ) : any ) ;
@@ -475,6 +488,13 @@ function createElement(
475
488
writable : true ,
476
489
value : true , // This element has already been validated on the server.
477
490
} ) ;
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
+ } ) ;
478
498
}
479
499
return element ;
480
500
}
@@ -487,6 +507,12 @@ function createLazyChunkWrapper<T>(
487
507
_payload : chunk ,
488
508
_init : readChunk ,
489
509
} ;
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
+ }
490
516
return lazyType;
491
517
}
492
518
@@ -682,7 +708,33 @@ function parseModelString(
682
708
// The status might have changed after initialization.
683
709
switch ( chunk . status ) {
684
710
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 ;
686
738
case PENDING :
687
739
case BLOCKED :
688
740
case CYCLIC :
@@ -959,6 +1011,24 @@ function resolveHint<Code: HintCode>(
959
1011
dispatchHint ( code , hintModel ) ;
960
1012
}
961
1013
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
+
962
1032
function mergeBuffer (
963
1033
buffer : Array < Uint8Array > ,
964
1034
lastChunk : Uint8Array ,
@@ -1052,7 +1122,7 @@ function processFullRow(
1052
1122
case 70 /* "F" */ :
1053
1123
resolveTypedArray ( response , id , buffer , chunk , Float32Array , 4 ) ;
1054
1124
return ;
1055
- case 68 /* "D " */ :
1125
+ case 100 /* "d " */ :
1056
1126
resolveTypedArray ( response , id , buffer , chunk , Float64Array , 8 ) ;
1057
1127
return ;
1058
1128
case 78 /* "N" */ :
@@ -1102,6 +1172,18 @@ function processFullRow(
1102
1172
resolveText ( response , id , row ) ;
1103
1173
return ;
1104
1174
}
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
+ }
1105
1187
case 80 /* "P" */ : {
1106
1188
if ( enablePostpone ) {
1107
1189
if ( __DEV__ ) {
@@ -1165,7 +1247,7 @@ export function processBinaryChunk(
1165
1247
resolvedRowTag === 76 /* "L" */ ||
1166
1248
resolvedRowTag === 108 /* "l" */ ||
1167
1249
resolvedRowTag === 70 /* "F" */ ||
1168
- resolvedRowTag === 68 /* "D " */ ||
1250
+ resolvedRowTag === 100 /* "d " */ ||
1169
1251
resolvedRowTag === 78 /* "N" */ ||
1170
1252
resolvedRowTag === 109 /* "m" */ ||
1171
1253
resolvedRowTag === 86 ) ) /* "V" */
0 commit comments