499
return new ReactPromise(ERRORED, null, error);
500
}
501
502
+function moveDebugInfoFromChunkToInnerValue<T>(
503
+ chunk: InitializedChunk<T>,
504
+ value: T,
505
+): void {
506
+ // Remove the debug info from the initialized chunk, and add it to the inner
507
+ // value instead. This can be a React element, an array, or an uninitialized
508
+ // Lazy.
509
+ const resolvedValue = resolveLazy(value);
510
+ if (
511
+ typeof resolvedValue === 'object' &&
512
+ resolvedValue !== null &&
513
+ (isArray(resolvedValue) ||
514
+ typeof resolvedValue[ASYNC_ITERATOR] === 'function' ||
515
+ resolvedValue.$$typeof === REACT_ELEMENT_TYPE ||
516
+ resolvedValue.$$typeof === REACT_LAZY_TYPE)
517
+ ) {
518
+ const debugInfo = chunk._debugInfo.splice(0);
519
+ if (isArray(resolvedValue._debugInfo)) {
520
+ // $FlowFixMe[method-unbinding]
521
+ resolvedValue._debugInfo.unshift.apply(
522
+ resolvedValue._debugInfo,
523
+ debugInfo,
524
+ );
525
+ } else {
526
+ Object.defineProperty((resolvedValue: any), '_debugInfo', {
527
+ configurable: false,
528
+ enumerable: false,
529
+ writable: true,
530
+ value: debugInfo,
531
+ });
532
+ }
533
+ }
534
+}
535
+
536
function wakeChunk<T>(
537
listeners: Array<InitializationReference | (T => mixed)>,
538
value: T,
505
- chunk: SomeChunk<T>,
539
+ chunk: InitializedChunk<T>,
540
): void {
541
for (let i = 0; i < listeners.length; i++) {
542
const listener = listeners[i];
546
fulfillReference(listener, value, chunk);
547
}
548
}
549
+
550
+ if (__DEV__) {
551
+ moveDebugInfoFromChunkToInnerValue(chunk, value);
552
+ }
553
}
554
555
function rejectChunk(
687
}
688
try {
689
initializeDebugChunk(response, chunk);
652
- chunk._debugChunk = null;
690
if (initializingHandler !== null) {
691
if (initializingHandler.errored) {
692
// Ignore error parsing debug info, we'll report the original error instead.
969
}
970
971
if (__DEV__) {
935
- // Lazily initialize any debug info and block the initializing chunk on any unresolved entries.
972
+ // Initialize any debug info and block the initializing chunk on any
973
+ // unresolved entries.
974
initializeDebugChunk(response, chunk);
937
- chunk._debugChunk = null;
975
}
976
977
try {
983
if (resolveListeners !== null) {
984
cyclicChunk.value = null;
985
cyclicChunk.reason = null;
949
- wakeChunk(resolveListeners, value, cyclicChunk);
986
+ for (let i = 0; i < resolveListeners.length; i++) {
987
+ const listener = resolveListeners[i];
988
+ if (typeof listener === 'function') {
989
+ listener(value);
990
+ } else {
991
+ fulfillReference(listener, value, cyclicChunk);
992
+ }
993
+ }
994
}
995
if (initializingHandler !== null) {
996
if (initializingHandler.errored) {
1007
const initializedChunk: InitializedChunk<T> = (chunk: any);
1008
initializedChunk.status = INITIALIZED;
1009
initializedChunk.value = value;
1010
+
1011
+ if (__DEV__) {
1012
+ moveDebugInfoFromChunkToInnerValue(initializedChunk, value);
1013
+ }
1014
} catch (error) {
1015
const erroredChunk: ErroredChunk<T> = (chunk: any);
1016
erroredChunk.status = ERRORED;
1127
function initializeElement(
1128
response: Response,
1129
element: any,
1082
- lazyType: null | LazyComponent<
1130
+ lazyNode: null | LazyComponent<
1131
React$Element<any>,
1132
SomeChunk<React$Element<any>>,
1133
>,
1199
initializeFakeStack(response, owner);
1200
}
1201
1154
- // In case the JSX runtime has validated the lazy type as a static child, we
1155
- // need to transfer this information to the element.
1156
- if (
1157
- lazyType &&
1158
- lazyType._store &&
1159
- lazyType._store.validated &&
1160
- !element._store.validated
1161
- ) {
1162
- element._store.validated = lazyType._store.validated;
1202
+ if (lazyNode !== null) {
1203
+ // In case the JSX runtime has validated the lazy type as a static child, we
1204
+ // need to transfer this information to the element.
1205
+ if (
1206
+ lazyNode._store &&
1207
+ lazyNode._store.validated &&
1208
+ !element._store.validated
1209
+ ) {
1210
+ element._store.validated = lazyNode._store.validated;
1211
+ }
1212
+
1213
+ // If the lazy node is initialized, we move its debug info to the inner
1214
+ // value.
1215
+ if (lazyNode._payload.status === INITIALIZED && lazyNode._debugInfo) {
1216
+ const debugInfo = lazyNode._debugInfo.splice(0);
1217
+ if (element._debugInfo) {
1218
+ // $FlowFixMe[method-unbinding]
1219
+ element._debugInfo.unshift.apply(element._debugInfo, debugInfo);
1220
+ } else {
1221
+ Object.defineProperty(element, '_debugInfo', {
1222
+ configurable: false,
1223
+ enumerable: false,
1224
+ writable: true,
1225
+ value: debugInfo,
1226
+ });
1227
+ }
1228
+ }
1229
}
1230
1231
// TODO: We should be freezing the element but currently, we might write into
1345
createBlockedChunk(response);
1346
handler.value = element;
1347
handler.chunk = blockedChunk;
1282
- const lazyType = createLazyChunkWrapper(blockedChunk, validated);
1348
+ const lazyNode = createLazyChunkWrapper(blockedChunk, validated);
1349
if (__DEV__) {
1350
// After we have initialized any blocked references, initialize stack etc.
1285
- const init = initializeElement.bind(null, response, element, lazyType);
1351
+ const init = initializeElement.bind(null, response, element, lazyNode);
1352
blockedChunk.then(init, init);
1353
}
1288
- return lazyType;
1354
+ return lazyNode;
1355
}
1356
}
1357
if (__DEV__) {
1532
const element: any = handler.value;
1533
switch (key) {
1534
case '3':
1469
- transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue);
1535
+ transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
1536
element.props = mappedValue;
1537
break;
1538
case '4':
1548
}
1549
break;
1550
default:
1485
- transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue);
1551
+ transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
1552
break;
1553
}
1554
} else if (__DEV__ && !reference.isDebug) {
1489
- transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue);
1555
+ transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
1556
}
1557
1558
handler.deps--;
1874
return (null: any);
1875
}
1876
1877
+function resolveLazy(value: any): mixed {
1878
+ while (
1879
+ typeof value === 'object' &&
1880
+ value !== null &&
1881
+ value.$$typeof === REACT_LAZY_TYPE
1882
+ ) {
1883
+ const payload: SomeChunk<any> = value._payload;
1884
+ if (payload.status === INITIALIZED) {
1885
+ value = payload.value;
1886
+ continue;
1887
+ }
1888
+ break;
1889
+ }
1890
+
1891
+ return value;
1892
+}
1893
+
1894
function transferReferencedDebugInfo(
1895
parentChunk: null | SomeChunk<any>,
1896
referencedChunk: SomeChunk<any>,
1814
- referencedValue: mixed,
1897
): void {
1898
if (__DEV__) {
1817
- const referencedDebugInfo = referencedChunk._debugInfo;
1818
- // If we have a direct reference to an object that was rendered by a synchronous
1819
- // server component, it might have some debug info about how it was rendered.
1820
- // We forward this to the underlying object. This might be a React Element or
1821
- // an Array fragment.
1822
- // If this was a string / number return value we lose the debug info. We choose
1823
- // that tradeoff to allow sync server components to return plain values and not
1824
- // use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
1825
- if (
1826
- typeof referencedValue === 'object' &&
1827
- referencedValue !== null &&
1828
- (isArray(referencedValue) ||
1829
- typeof referencedValue[ASYNC_ITERATOR] === 'function' ||
1830
- referencedValue.$$typeof === REACT_ELEMENT_TYPE)
1831
- ) {
1832
- // We should maybe use a unique symbol for arrays but this is a React owned array.
1833
- // $FlowFixMe[prop-missing]: This should be added to elements.
1834
- const existingDebugInfo: ?ReactDebugInfo =
1835
- (referencedValue._debugInfo: any);
1836
- if (existingDebugInfo == null) {
1837
- Object.defineProperty((referencedValue: any), '_debugInfo', {
1838
- configurable: false,
1839
- enumerable: false,
1840
- writable: true,
1841
- value: referencedDebugInfo.slice(0), // Clone so that pushing later isn't going into the original
1842
- });
1843
- } else {
1844
- // $FlowFixMe[method-unbinding]
1845
- existingDebugInfo.push.apply(existingDebugInfo, referencedDebugInfo);
1846
- }
1847
- }
1848
- // We also add the debug info to the initializing chunk since the resolution of that promise is
1849
- // also blocked by the referenced debug info. By adding it to both we can track it even if the array/element
1850
- // is extracted, or if the root is rendered as is.
1899
+ // We add the debug info to the initializing chunk since the resolution of
1900
+ // that promise is also blocked by the referenced debug info. By adding it
1901
+ // to both we can track it even if the array/element/lazy is extracted, or
1902
+ // if the root is rendered as is.
1903
if (parentChunk !== null) {
1904
+ const referencedDebugInfo = referencedChunk._debugInfo;
1905
const parentDebugInfo = parentChunk._debugInfo;
1906
for (let i = 0; i < referencedDebugInfo.length; ++i) {
1907
const debugInfoEntry = referencedDebugInfo[i];
2052
// If we're resolving the "owner" or "stack" slot of an Element array, we don't call
2053
// transferReferencedDebugInfo because this reference is to a debug chunk.
2054
} else {
2002
- transferReferencedDebugInfo(initializingChunk, chunk, chunkValue);
2055
+ transferReferencedDebugInfo(initializingChunk, chunk);
2056
}
2057
return chunkValue;
2058
case PENDING:
2762
}
2763
}
2764
2765
+function addDebugInfo(chunk: SomeChunk<any>, debugInfo: ReactDebugInfo): void {
2766
+ const value = resolveLazy(chunk.value);
2767
+ if (
2768
+ typeof value === 'object' &&
2769
+ value !== null &&
2770
+ (isArray(value) ||
2771
+ typeof value[ASYNC_ITERATOR] === 'function' ||
2772
+ value.$$typeof === REACT_ELEMENT_TYPE ||
2773
+ value.$$typeof === REACT_LAZY_TYPE)
2774
+ ) {
2775
+ if (isArray(value._debugInfo)) {
2776
+ // $FlowFixMe[method-unbinding]
2777
+ value._debugInfo.push.apply(value._debugInfo, debugInfo);
2778
+ } else {
2779
+ Object.defineProperty((value: any), '_debugInfo', {
2780
+ configurable: false,
2781
+ enumerable: false,
2782
+ writable: true,
2783
+ value: debugInfo,
2784
+ });
2785
+ }
2786
+ } else {
2787
+ // $FlowFixMe[method-unbinding]
2788
+ chunk._debugInfo.push.apply(chunk._debugInfo, debugInfo);
2789
+ }
2790
+}
2791
+
2792
function resolveChunkDebugInfo(
2793
streamState: StreamState,
2794
chunk: SomeChunk<any>,
2795
): void {
2796
if (__DEV__ && enableAsyncDebugInfo) {
2717
- // Push the currently resolving chunk's debug info representing the stream on the Promise
2718
- // that was waiting on the stream.
2719
- chunk._debugInfo.push({awaited: streamState._debugInfo});
2797
+ // Add the currently resolving chunk's debug info representing the stream
2798
+ // to the Promise that was waiting on the stream, or its underlying value.
2799
+ const debugInfo: ReactDebugInfo = [{awaited: streamState._debugInfo}];
2800
+ if (chunk.status === PENDING || chunk.status === BLOCKED) {
2801
+ const boundAddDebugInfo = addDebugInfo.bind(null, chunk, debugInfo);
2802
+ chunk.then(boundAddDebugInfo, boundAddDebugInfo);
2803
+ } else {
2804
+ addDebugInfo(chunk, debugInfo);
2805
+ }
2806
}
2807
}
2808
2995
const resolveListeners = chunk.value;
2996
2997
if (__DEV__) {
2912
- // Lazily initialize any debug info and block the initializing chunk on any unresolved entries.
2998
+ // Initialize any debug info and block the initializing chunk on any
2999
+ // unresolved entries.
3000
if (chunk._debugChunk != null) {
3001
const prevHandler = initializingHandler;
3002
const prevChunk = initializingChunk;
3010
}
3011
try {
3012
initializeDebugChunk(response, chunk);
2926
- chunk._debugChunk = null;
3013
if (initializingHandler !== null) {
3014
if (initializingHandler.errored) {
3015
// Ignore error parsing debug info, we'll report the original error instead.
3033
resolvedChunk.value = stream;
3034
resolvedChunk.reason = controller;
3035
if (resolveListeners !== null) {
2950
- wakeChunk(resolveListeners, chunk.value, chunk);
3036
+ wakeChunk(resolveListeners, chunk.value, (chunk: any));
3037
}
3038
}
3039