@@ -1105,6 +1105,143 @@ function callWithDebugContextInDEV<A, T>(
1105
1105
1106
1106
const voidHandler = ( ) => { } ;
1107
1107
1108
+ function processServerComponentReturnValue(
1109
+ request: Request,
1110
+ task: Task,
1111
+ Component: any,
1112
+ result: any,
1113
+ ): any {
1114
+ // A Server Component's return value has a few special properties due to being
1115
+ // in the return position of a Component. We convert them here.
1116
+ if (
1117
+ typeof result !== 'object' ||
1118
+ result === null ||
1119
+ isClientReference ( result )
1120
+ ) {
1121
+ return result ;
1122
+ }
1123
+
1124
+ if (typeof result.then === 'function') {
1125
+ // When the return value is in children position we can resolve it immediately,
1126
+ // to its value without a wrapper if it's synchronously available.
1127
+ const thenable : Thenable < any > = result ;
1128
+ if ( __DEV__ ) {
1129
+ // If the thenable resolves to an element, then it was in a static position,
1130
+ // the return value of a Server Component. That doesn't need further validation
1131
+ // of keys. The Server Component itself would have had a key.
1132
+ thenable. then ( resolvedValue => {
1133
+ if (
1134
+ typeof resolvedValue === 'object' &&
1135
+ resolvedValue !== null &&
1136
+ resolvedValue . $$typeof === REACT_ELEMENT_TYPE
1137
+ ) {
1138
+ resolvedValue . _store . validated = 1 ;
1139
+ }
1140
+ } , voidHandler ) ;
1141
+ }
1142
+ if (thenable.status === 'fulfilled') {
1143
+ return thenable . value ;
1144
+ }
1145
+ // TODO: Once we accept Promises as children on the client, we can just return
1146
+ // the thenable here.
1147
+ return createLazyWrapperAroundWakeable(result);
1148
+ }
1149
+
1150
+ if ( __DEV__ ) {
1151
+ if ( ( result : any ) . $$typeof === REACT_ELEMENT_TYPE ) {
1152
+ // If the server component renders to an element, then it was in a static position.
1153
+ // That doesn't need further validation of keys. The Server Component itself would
1154
+ // have had a key.
1155
+ ( result : any ) . _store . validated = 1 ;
1156
+ }
1157
+ }
1158
+
1159
+ // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible
1160
+ // to be rendered as a React Child. However, because we have the function to recreate
1161
+ // an iterable from rendering the element again, we can effectively treat it as multi-
1162
+ // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by
1163
+ // adding a wrapper so that this component effectively renders down to an AsyncIterable.
1164
+ const iteratorFn = getIteratorFn ( result ) ;
1165
+ if ( iteratorFn ) {
1166
+ const iterableChild = result ;
1167
+ const multiShot = {
1168
+ [ Symbol . iterator ] : function ( ) {
1169
+ const iterator = iteratorFn . call ( iterableChild ) ;
1170
+ if ( __DEV__ ) {
1171
+ // If this was an Iterator but not a GeneratorFunction we warn because
1172
+ // it might have been a mistake. Technically you can make this mistake with
1173
+ // GeneratorFunctions and even single-shot Iterables too but it's extra
1174
+ // tempting to try to return the value from a generator.
1175
+ if ( iterator === iterableChild ) {
1176
+ const isGeneratorComponent =
1177
+ // $FlowIgnore[method-unbinding]
1178
+ Object . prototype . toString . call ( Component ) ===
1179
+ '[object GeneratorFunction]' &&
1180
+ // $FlowIgnore[method-unbinding]
1181
+ Object . prototype . toString . call ( iterableChild ) ===
1182
+ '[object Generator]' ;
1183
+ if ( ! isGeneratorComponent ) {
1184
+ callWithDebugContextInDEV ( request , task , ( ) => {
1185
+ console . error (
1186
+ 'Returning an Iterator from a Server Component is not supported ' +
1187
+ 'since it cannot be looped over more than once. ' ,
1188
+ ) ;
1189
+ } ) ;
1190
+ }
1191
+ }
1192
+ }
1193
+ return ( iterator : any ) ;
1194
+ } ,
1195
+ } ;
1196
+ if ( __DEV__ ) {
1197
+ ( multiShot : any ) . _debugInfo = iterableChild . _debugInfo ;
1198
+ }
1199
+ return multiShot;
1200
+ }
1201
+ if (
1202
+ enableFlightReadableStream &&
1203
+ typeof ( result : any ) [ ASYNC_ITERATOR ] === 'function ' &&
1204
+ ( typeof ReadableStream !== 'function ' ||
1205
+ ! ( result instanceof ReadableStream ) )
1206
+ ) {
1207
+ const iterableChild = result ;
1208
+ const multishot = {
1209
+ [ ASYNC_ITERATOR ] : function ( ) {
1210
+ const iterator = ( iterableChild : any ) [ ASYNC_ITERATOR ] ( ) ;
1211
+ if ( __DEV__ ) {
1212
+ // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because
1213
+ // it might have been a mistake. Technically you can make this mistake with
1214
+ // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra
1215
+ // tempting to try to return the value from a generator.
1216
+ if ( iterator === iterableChild ) {
1217
+ const isGeneratorComponent =
1218
+ // $FlowIgnore[method-unbinding]
1219
+ Object . prototype . toString . call ( Component ) ===
1220
+ '[object AsyncGeneratorFunction]' &&
1221
+ // $FlowIgnore[method-unbinding]
1222
+ Object . prototype . toString . call ( iterableChild ) ===
1223
+ '[object AsyncGenerator]' ;
1224
+ if ( ! isGeneratorComponent ) {
1225
+ callWithDebugContextInDEV ( request , task , ( ) => {
1226
+ console . error (
1227
+ 'Returning an AsyncIterator from a Server Component is not supported ' +
1228
+ 'since it cannot be looped over more than once. ' ,
1229
+ ) ;
1230
+ } ) ;
1231
+ }
1232
+ }
1233
+ }
1234
+ return iterator ;
1235
+ } ,
1236
+ } ;
1237
+ if ( __DEV__ ) {
1238
+ ( multishot : any ) . _debugInfo = iterableChild . _debugInfo ;
1239
+ }
1240
+ return multishot;
1241
+ }
1242
+ return result ;
1243
+ }
1244
+
1108
1245
function renderFunctionComponent < Props > (
1109
1246
request: Request,
1110
1247
task: Task,
@@ -1231,123 +1368,9 @@ function renderFunctionComponent<Props>(
1231
1368
throw null;
1232
1369
}
1233
1370
1234
- if (
1235
- typeof result === 'object ' &&
1236
- result !== null &&
1237
- ! isClientReference ( result )
1238
- ) {
1239
- if ( typeof result . then === 'function' ) {
1240
- // When the return value is in children position we can resolve it immediately,
1241
- // to its value without a wrapper if it's synchronously available.
1242
- const thenable : Thenable < any > = result ;
1243
- if ( __DEV__ ) {
1244
- // If the thenable resolves to an element, then it was in a static position,
1245
- // the return value of a Server Component. That doesn't need further validation
1246
- // of keys. The Server Component itself would have had a key.
1247
- thenable. then ( resolvedValue => {
1248
- if (
1249
- typeof resolvedValue === 'object' &&
1250
- resolvedValue !== null &&
1251
- resolvedValue . $$typeof === REACT_ELEMENT_TYPE
1252
- ) {
1253
- resolvedValue . _store . validated = 1 ;
1254
- }
1255
- } , voidHandler ) ;
1256
- }
1257
- if (thenable.status === 'fulfilled') {
1258
- return thenable . value ;
1259
- }
1260
- // TODO: Once we accept Promises as children on the client, we can just return
1261
- // the thenable here.
1262
- result = createLazyWrapperAroundWakeable(result);
1263
- }
1371
+ // Apply special cases.
1372
+ result = processServerComponentReturnValue ( request , task , Component , result ) ;
1264
1373
1265
- // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible
1266
- // to be rendered as a React Child. However, because we have the function to recreate
1267
- // an iterable from rendering the element again, we can effectively treat it as multi-
1268
- // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by
1269
- // adding a wrapper so that this component effectively renders down to an AsyncIterable.
1270
- const iteratorFn = getIteratorFn ( result ) ;
1271
- if ( iteratorFn ) {
1272
- const iterableChild = result ;
1273
- result = {
1274
- [ Symbol . iterator ] : function ( ) {
1275
- const iterator = iteratorFn . call ( iterableChild ) ;
1276
- if ( __DEV__ ) {
1277
- // If this was an Iterator but not a GeneratorFunction we warn because
1278
- // it might have been a mistake. Technically you can make this mistake with
1279
- // GeneratorFunctions and even single-shot Iterables too but it's extra
1280
- // tempting to try to return the value from a generator.
1281
- if ( iterator === iterableChild ) {
1282
- const isGeneratorComponent =
1283
- // $FlowIgnore[method-unbinding]
1284
- Object . prototype . toString . call ( Component ) ===
1285
- '[object GeneratorFunction]' &&
1286
- // $FlowIgnore[method-unbinding]
1287
- Object . prototype . toString . call ( iterableChild ) ===
1288
- '[object Generator]' ;
1289
- if ( ! isGeneratorComponent ) {
1290
- callWithDebugContextInDEV ( request , task , ( ) => {
1291
- console . error (
1292
- 'Returning an Iterator from a Server Component is not supported ' +
1293
- 'since it cannot be looped over more than once. ' ,
1294
- ) ;
1295
- } ) ;
1296
- }
1297
- }
1298
- }
1299
- return ( iterator : any ) ;
1300
- } ,
1301
- } ;
1302
- if ( __DEV__ ) {
1303
- ( result : any ) . _debugInfo = iterableChild . _debugInfo ;
1304
- }
1305
- } else if (
1306
- enableFlightReadableStream &&
1307
- typeof ( result : any ) [ ASYNC_ITERATOR ] === 'function ' &&
1308
- ( typeof ReadableStream !== 'function ' ||
1309
- ! ( result instanceof ReadableStream ) )
1310
- ) {
1311
- const iterableChild = result ;
1312
- result = {
1313
- [ ASYNC_ITERATOR ] : function ( ) {
1314
- const iterator = ( iterableChild : any ) [ ASYNC_ITERATOR ] ( ) ;
1315
- if ( __DEV__ ) {
1316
- // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because
1317
- // it might have been a mistake. Technically you can make this mistake with
1318
- // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra
1319
- // tempting to try to return the value from a generator.
1320
- if ( iterator === iterableChild ) {
1321
- const isGeneratorComponent =
1322
- // $FlowIgnore[method-unbinding]
1323
- Object . prototype . toString . call ( Component ) ===
1324
- '[object AsyncGeneratorFunction]' &&
1325
- // $FlowIgnore[method-unbinding]
1326
- Object . prototype . toString . call ( iterableChild ) ===
1327
- '[object AsyncGenerator]' ;
1328
- if ( ! isGeneratorComponent ) {
1329
- callWithDebugContextInDEV ( request , task , ( ) => {
1330
- console . error (
1331
- 'Returning an AsyncIterator from a Server Component is not supported ' +
1332
- 'since it cannot be looped over more than once. ' ,
1333
- ) ;
1334
- } ) ;
1335
- }
1336
- }
1337
- }
1338
- return iterator ;
1339
- } ,
1340
- } ;
1341
- if ( __DEV__ ) {
1342
- ( result : any ) . _debugInfo = iterableChild . _debugInfo ;
1343
- }
1344
- } else if ( __DEV__ && ( result : any ) . $$typeof === REACT_ELEMENT_TYPE ) {
1345
- // If the server component renders to an element, then it was in a static position.
1346
- // That doesn't need further validation of keys. The Server Component itself would
1347
- // have had a key.
1348
- ( result : any ) . _store . validated = 1 ;
1349
- }
1350
- }
1351
1374
// Track this element's key on the Server Component on the keyPath context..
1352
1375
const prevKeyPath = task . keyPath ;
1353
1376
const prevImplicitSlot = task . implicitSlot ;
0 commit comments