@@ -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,14 @@ 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
+ } ) ;
498
+ // self and source are DEV only properties.
478
499
Object . defineProperty ( element , '_self' , {
479
500
configurable : false ,
480
501
enumerable : false ,
@@ -499,6 +520,12 @@ function createLazyChunkWrapper<T>(
499
520
_payload : chunk ,
500
521
_init : readChunk ,
501
522
} ;
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
+ }
502
529
return lazyType;
503
530
}
504
531
@@ -694,7 +721,33 @@ function parseModelString(
694
721
// The status might have changed after initialization.
695
722
switch ( chunk . status ) {
696
723
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 ;
698
751
case PENDING :
699
752
case BLOCKED :
700
753
case CYCLIC :
@@ -971,6 +1024,24 @@ function resolveHint<Code: HintCode>(
971
1024
dispatchHint ( code , hintModel ) ;
972
1025
}
973
1026
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
+
974
1045
function mergeBuffer (
975
1046
buffer : Array < Uint8Array > ,
976
1047
lastChunk : Uint8Array ,
@@ -1064,7 +1135,7 @@ function processFullRow(
1064
1135
case 70 /* "F" */ :
1065
1136
resolveTypedArray ( response , id , buffer , chunk , Float32Array , 4 ) ;
1066
1137
return ;
1067
- case 68 /* "D " */ :
1138
+ case 100 /* "d " */ :
1068
1139
resolveTypedArray ( response , id , buffer , chunk , Float64Array , 8 ) ;
1069
1140
return ;
1070
1141
case 78 /* "N" */ :
@@ -1114,6 +1185,18 @@ function processFullRow(
1114
1185
resolveText ( response , id , row ) ;
1115
1186
return ;
1116
1187
}
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
+ }
1117
1200
case 80 /* "P" */ : {
1118
1201
if ( enablePostpone ) {
1119
1202
if ( __DEV__ ) {
@@ -1177,7 +1260,7 @@ export function processBinaryChunk(
1177
1260
resolvedRowTag === 76 /* "L" */ ||
1178
1261
resolvedRowTag === 108 /* "l" */ ||
1179
1262
resolvedRowTag === 70 /* "F" */ ||
1180
- resolvedRowTag === 68 /* "D " */ ||
1263
+ resolvedRowTag === 100 /* "d " */ ||
1181
1264
resolvedRowTag === 78 /* "N" */ ||
1182
1265
resolvedRowTag === 109 /* "m" */ ||
1183
1266
resolvedRowTag === 86 ) ) /* "V" */
0 commit comments