[Flight] Always initialize a debug info array for each Chunk (#34419)
I'm about to add info for pretty much all of these anyway since they all depend on the data stream itself.
Sebastian Markbåge committed
Sep 8, 2025 at 12:28 UTC
294c33f34da0b5f908946c9add86f58426e7da5f
1 file changed
+31
-34
packages/react-client/src/ReactFlightClient.js
+31
-34
@@ -169,7 +169,7 @@ type PendingChunk<T> = {
169
reason: null | Array<InitializationReference | (mixed => mixed)>,
170
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
171
_debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only
172
- _debugInfo: null | ReactDebugInfo, // DEV-only
172
+ _debugInfo: ReactDebugInfo, // DEV-only
173
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
174
};
175
type BlockedChunk<T> = {
@@ -178,7 +178,7 @@ type BlockedChunk<T> = {
178
reason: null | Array<InitializationReference | (mixed => mixed)>,
179
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
180
_debugChunk: null, // DEV-only
181
- _debugInfo: null | ReactDebugInfo, // DEV-only
181
+ _debugInfo: ReactDebugInfo, // DEV-only
182
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
183
};
184
type ResolvedModelChunk<T> = {
@@ -187,7 +187,7 @@ type ResolvedModelChunk<T> = {
187
reason: Response,
188
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
189
_debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only
190
- _debugInfo: null | ReactDebugInfo, // DEV-only
190
+ _debugInfo: ReactDebugInfo, // DEV-only
191
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
192
};
193
type ResolvedModuleChunk<T> = {
@@ -196,7 +196,7 @@ type ResolvedModuleChunk<T> = {
196
reason: null,
197
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
198
_debugChunk: null, // DEV-only
199
- _debugInfo: null | ReactDebugInfo, // DEV-only
199
+ _debugInfo: ReactDebugInfo, // DEV-only
200
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
201
};
202
type InitializedChunk<T> = {
@@ -205,7 +205,7 @@ type InitializedChunk<T> = {
205
reason: null | FlightStreamController,
206
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
207
_debugChunk: null, // DEV-only
208
- _debugInfo: null | ReactDebugInfo, // DEV-only
208
+ _debugInfo: ReactDebugInfo, // DEV-only
209
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
210
};
211
type InitializedStreamChunk<
@@ -216,7 +216,7 @@ type InitializedStreamChunk<
216
reason: FlightStreamController,
217
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
218
_debugChunk: null, // DEV-only
219
- _debugInfo: null | ReactDebugInfo, // DEV-only
219
+ _debugInfo: ReactDebugInfo, // DEV-only
220
then(resolve: (ReadableStream) => mixed, reject?: (mixed) => mixed): void,
221
};
222
type ErroredChunk<T> = {
@@ -225,7 +225,7 @@ type ErroredChunk<T> = {
225
reason: mixed,
226
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
227
_debugChunk: null, // DEV-only
228
- _debugInfo: null | ReactDebugInfo, // DEV-only
228
+ _debugInfo: ReactDebugInfo, // DEV-only
229
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
230
};
231
type HaltedChunk<T> = {
@@ -234,7 +234,7 @@ type HaltedChunk<T> = {
234
reason: null,
235
_children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
236
_debugChunk: null, // DEV-only
237
- _debugInfo: null | ReactDebugInfo, // DEV-only
237
+ _debugInfo: ReactDebugInfo, // DEV-only
238
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
239
};
240
type SomeChunk<T> =
@@ -256,7 +256,7 @@ function ReactPromise(status: any, value: any, reason: any) {
256
}
257
if (__DEV__) {
258
this._debugChunk = null;
259
- this._debugInfo = null;
259
+ this._debugInfo = [];
260
}
261
}
262
// We subclass Promise.prototype so that we get other methods like .catch
@@ -798,12 +798,10 @@ function resolveModuleChunk<T>(
798
resolvedChunk.value = value;
799
if (__DEV__) {
800
const debugInfo = getModuleDebugInfo(value);
801
- if (debugInfo !== null && resolvedChunk._debugInfo != null) {
801
+ if (debugInfo !== null) {
802
// Add to the live set if it was already initialized.
803
// $FlowFixMe[method-unbinding]
804
resolvedChunk._debugInfo.push.apply(resolvedChunk._debugInfo, debugInfo);
805
- } else {
806
- resolvedChunk._debugInfo = debugInfo;
805
}
806
}
807
if (resolveListeners !== null) {
@@ -842,7 +840,7 @@ function initializeDebugChunk(
840
): void {
841
const debugChunk = chunk._debugChunk;
842
if (debugChunk !== null) {
845
- const debugInfo = chunk._debugInfo || (chunk._debugInfo = []);
843
+ const debugInfo = chunk._debugInfo;
844
try {
845
if (debugChunk.status === RESOLVED_MODEL) {
846
// Find the index of this debug info by walking the linked list.
@@ -1303,10 +1301,8 @@ function createLazyChunkWrapper<T>(
1301
_init: readChunk,
1302
};
1303
if (__DEV__) {
1306
- // Ensure we have a live array to track future debug info.
1307
- const chunkDebugInfo: ReactDebugInfo =
1308
- chunk._debugInfo || (chunk._debugInfo = ([]: ReactDebugInfo));
1309
- lazyType._debugInfo = chunkDebugInfo;
1304
+ // Forward the live array
1305
+ lazyType._debugInfo = chunk._debugInfo;
1306
// Initialize a store for key validation by the JSX runtime.
1307
lazyType._store = {validated: validated};
1308
}
@@ -1508,9 +1504,7 @@ function rejectReference(
1504
// $FlowFixMe[cannot-write]
1505
erroredComponent.debugTask = element._debugTask;
1506
}
1511
- const chunkDebugInfo: ReactDebugInfo =
1512
- chunk._debugInfo || (chunk._debugInfo = []);
1513
- chunkDebugInfo.push(erroredComponent);
1507
+ chunk._debugInfo.push(erroredComponent);
1508
}
1509
}
1510
@@ -1750,9 +1744,7 @@ function loadServerReference<A: Iterable<any>, T>(
1744
// $FlowFixMe[cannot-write]
1745
erroredComponent.debugTask = element._debugTask;
1746
}
1753
- const chunkDebugInfo: ReactDebugInfo =
1754
- chunk._debugInfo || (chunk._debugInfo = []);
1755
- chunkDebugInfo.push(erroredComponent);
1747
+ chunk._debugInfo.push(erroredComponent);
1748
}
1749
}
1750
@@ -1770,7 +1762,7 @@ function transferReferencedDebugInfo(
1762
referencedChunk: SomeChunk<any>,
1763
referencedValue: mixed,
1764
): void {
1773
- if (__DEV__ && referencedChunk._debugInfo) {
1765
+ if (__DEV__) {
1766
const referencedDebugInfo = referencedChunk._debugInfo;
1767
// If we have a direct reference to an object that was rendered by a synchronous
1768
// server component, it might have some debug info about how it was rendered.
@@ -1784,24 +1776,29 @@ function transferReferencedDebugInfo(
1776
referencedValue !== null &&
1777
(isArray(referencedValue) ||
1778
typeof referencedValue[ASYNC_ITERATOR] === 'function' ||
1787
- referencedValue.$$typeof === REACT_ELEMENT_TYPE) &&
1788
- !referencedValue._debugInfo
1779
+ referencedValue.$$typeof === REACT_ELEMENT_TYPE)
1780
) {
1781
// We should maybe use a unique symbol for arrays but this is a React owned array.
1782
// $FlowFixMe[prop-missing]: This should be added to elements.
1792
- Object.defineProperty((referencedValue: any), '_debugInfo', {
1793
- configurable: false,
1794
- enumerable: false,
1795
- writable: true,
1796
- value: referencedDebugInfo,
1797
- });
1783
+ const existingDebugInfo: ?ReactDebugInfo =
1784
+ (referencedValue._debugInfo: any);
1785
+ if (existingDebugInfo == null) {
1786
+ Object.defineProperty((referencedValue: any), '_debugInfo', {
1787
+ configurable: false,
1788
+ enumerable: false,
1789
+ writable: true,
1790
+ value: referencedDebugInfo.slice(0), // Clone so that pushing later isn't going into the original
1791
+ });
1792
+ } else {
1793
+ // $FlowFixMe[method-unbinding]
1794
+ existingDebugInfo.push.apply(existingDebugInfo, referencedDebugInfo);
1795
+ }
1796
}
1797
// We also add it to the initializing chunk since the resolution of that promise is
1798
// also blocked by these. By adding it to both we can track it even if the array/element
1799
// is extracted, or if the root is rendered as is.
1800
if (parentChunk !== null) {
1803
- const parentDebugInfo =
1804
- parentChunk._debugInfo || (parentChunk._debugInfo = []);
1801
+ const parentDebugInfo = parentChunk._debugInfo;
1802
// $FlowFixMe[method-unbinding]
1803
parentDebugInfo.push.apply(parentDebugInfo, referencedDebugInfo);
1804
}