@samitouri / QOS-React / commits / 294c33f34d

[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 }