216
const jsxPropsParents: WeakMap<any, any> = new WeakMap();
217
const jsxChildrenParents: WeakMap<any, any> = new WeakMap();
218
219
+function serializeThenable(request: Request, thenable: Thenable<any>): number {
220
+ request.pendingChunks++;
221
+ const newTask = createTask(
222
+ request,
223
+ null,
224
+ getActiveContext(),
225
+ request.abortableTasks,
226
+ );
227
+
228
+ switch (thenable.status) {
229
+ case 'fulfilled': {
230
+ // We have the resolved value, we can go ahead and schedule it for serialization.
231
+ newTask.model = thenable.value;
232
+ pingTask(request, newTask);
233
+ return newTask.id;
234
+ }
235
+ case 'rejected': {
236
+ const x = thenable.reason;
237
+ const digest = logRecoverableError(request, x);
238
+ if (__DEV__) {
239
+ const {message, stack} = getErrorMessageAndStackDev(x);
240
+ emitErrorChunkDev(request, newTask.id, digest, message, stack);
241
+ } else {
242
+ emitErrorChunkProd(request, newTask.id, digest);
243
+ }
244
+ return newTask.id;
245
+ }
246
+ default: {
247
+ if (typeof thenable.status === 'string') {
248
+ // Only instrument the thenable if the status if not defined. If
249
+ // it's defined, but an unknown value, assume it's been instrumented by
250
+ // some custom userspace implementation. We treat it as "pending".
251
+ break;
252
+ }
253
+ const pendingThenable: PendingThenable<mixed> = (thenable: any);
254
+ pendingThenable.status = 'pending';
255
+ pendingThenable.then(
256
+ fulfilledValue => {
257
+ if (thenable.status === 'pending') {
258
+ const fulfilledThenable: FulfilledThenable<mixed> = (thenable: any);
259
+ fulfilledThenable.status = 'fulfilled';
260
+ fulfilledThenable.value = fulfilledValue;
261
+ }
262
+ },
263
+ (error: mixed) => {
264
+ if (thenable.status === 'pending') {
265
+ const rejectedThenable: RejectedThenable<mixed> = (thenable: any);
266
+ rejectedThenable.status = 'rejected';
267
+ rejectedThenable.reason = error;
268
+ }
269
+ },
270
+ );
271
+ break;
272
+ }
273
+ }
274
+
275
+ thenable.then(
276
+ value => {
277
+ newTask.model = value;
278
+ pingTask(request, newTask);
279
+ },
280
+ reason => {
281
+ // TODO: Is it safe to directly emit these without being inside a retry?
282
+ const digest = logRecoverableError(request, reason);
283
+ if (__DEV__) {
284
+ const {message, stack} = getErrorMessageAndStackDev(reason);
285
+ emitErrorChunkDev(request, newTask.id, digest, message, stack);
286
+ } else {
287
+ emitErrorChunkProd(request, newTask.id, digest);
288
+ }
289
+ },
290
+ );
291
+
292
+ return newTask.id;
293
+}
294
+
295
function readThenable<T>(thenable: Thenable<T>): T {
296
if (thenable.status === 'fulfilled') {
297
return thenable.value;
346
}
347
348
function attemptResolveElement(
349
+ request: Request,
350
type: any,
351
key: null | React$Key,
352
ref: mixed,
380
result !== null &&
381
typeof result.then === 'function'
382
) {
383
+ // When the return value is in children position we can resolve it immediately,
384
+ // to its value without a wrapper if it's synchronously available.
385
+ const thenable: Thenable<any> = result;
386
+ if (thenable.status === 'fulfilled') {
387
+ return thenable.value;
388
+ }
389
+ // TODO: Once we accept Promises as children on the client, we can just return
390
+ // the thenable here.
391
return createLazyWrapperAroundWakeable(result);
392
}
393
return result;
416
const init = type._init;
417
const wrappedType = init(payload);
418
return attemptResolveElement(
419
+ request,
420
wrappedType,
421
key,
422
ref,
431
}
432
case REACT_MEMO_TYPE: {
433
return attemptResolveElement(
434
+ request,
435
type.type,
436
key,
437
ref,
501
return '$' + id.toString(16);
502
}
503
417
-function serializeByRefID(id: number): string {
504
+function serializeLazyID(id: number): string {
505
return '$L' + id.toString(16);
506
}
507
508
+function serializePromiseID(id: number): string {
509
+ return '$@' + id.toString(16);
510
+}
511
+
512
function serializeSymbolReference(name: string): string {
513
return '$S' + name;
514
}
533
// knows how to deal with lazy values. This lets us suspend
534
// on this component rather than its parent until the code has
535
// loaded.
445
- return serializeByRefID(existingId);
536
+ return serializeLazyID(existingId);
537
}
538
return serializeByValueID(existingId);
539
}
552
// knows how to deal with lazy values. This lets us suspend
553
// on this component rather than its parent until the code has
554
// loaded.
464
- return serializeByRefID(moduleId);
555
+ return serializeLazyID(moduleId);
556
}
557
return serializeByValueID(moduleId);
558
} catch (x) {
926
const element: React$Element<any> = (value: any);
927
// Attempt to render the Server Component.
928
value = attemptResolveElement(
929
+ request,
930
element.type,
931
element.key,
932
element.ref,
965
const ping = newTask.ping;
966
x.then(ping, ping);
967
newTask.thenableState = getThenableStateAfterSuspending();
876
- return serializeByRefID(newTask.id);
968
+ return serializeLazyID(newTask.id);
969
} else {
970
// Something errored. We'll still send everything we have up until this point.
971
// We'll replace this element with a lazy reference that throws on the client
979
} else {
980
emitErrorChunkProd(request, errorId, digest);
981
}
890
- return serializeByRefID(errorId);
982
+ return serializeLazyID(errorId);
983
}
984
}
985
}
991
if (typeof value === 'object') {
992
if (isClientReference(value)) {
993
return serializeClientReference(request, parent, key, (value: any));
994
+ } else if (typeof value.then === 'function') {
995
+ // We assume that any object with a .then property is a "Thenable" type,
996
+ // or a Promise type. Either of which can be represented by a Promise.
997
+ const promiseId = serializeThenable(request, (value: any));
998
+ return serializePromiseID(promiseId);
999
} else if ((value: any).$$typeof === REACT_PROVIDER_TYPE) {
1000
const providerKey = ((value: any): ReactProviderType<any>)._context
1001
._globalName;
1254
// also suspends.
1255
task.model = value;
1256
value = attemptResolveElement(
1257
+ request,
1258
element.type,
1259
element.key,
1260
element.ref,
1278
const nextElement: React$Element<any> = (value: any);
1279
task.model = value;
1280
value = attemptResolveElement(
1281
+ request,
1282
nextElement.type,
1283
nextElement.key,
1284
nextElement.ref,