From 0c89b160f6382814aa02cc469a80f59c720ab6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 18 Aug 2025 11:34:00 -0400 Subject: [PATCH] [Flight] Add DebugInfo for Bundler Chunks (#34226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a "suspended by" row for each chunk that is referenced from a client reference. So when you select a client component, you can see what bundles will block that client component when loading on the client. This is only done in the browser build since if we added it on the server, it would show up as a blocking resource and while it's possible we expect that a typical server request won't block on loading JS. Screenshot 2025-08-17 at 3 45 14 PM Screenshot 2025-08-17 at 3 46 58 PM Currently this is only included if it ends up wrapped in a lazy like in the typical type position of a Client Component, but there's a general issue that maybe hard references need to transfer their debug info to the parent which can transfer it to the Fiber. --- .eslintrc.js | 1 + .../react-client/src/ReactFlightClient.js | 23 +++- .../forks/ReactFlightClientConfig.custom.js | 1 + .../forks/ReactFlightClientConfig.dom-bun.js | 1 + .../ReactFlightClientConfig.dom-legacy.js | 1 + .../forks/ReactFlightClientConfig.markup.js | 6 ++ .../ReactFlightClientConfigBundlerESM.js | 94 ++++++++++++++++ .../ReactFlightClientConfigBundlerParcel.js | 9 +- ...ReactFlightClientConfigBundlerTurbopack.js | 22 +++- ...ightClientConfigBundlerTurbopackBrowser.js | 96 +++++++++++++++++ ...lightClientConfigBundlerTurbopackServer.js | 10 ++ .../src/__tests__/utils/WebpackMock.js | 3 + .../ReactFlightClientConfigBundlerNode.js | 6 ++ .../ReactFlightClientConfigBundlerWebpack.js | 23 +++- ...FlightClientConfigBundlerWebpackBrowser.js | 101 ++++++++++++++++++ ...tFlightClientConfigBundlerWebpackServer.js | 11 ++ scripts/flow/environment.js | 1 + scripts/rollup/validate/eslintrc.cjs.js | 1 + scripts/rollup/validate/eslintrc.cjs2015.js | 1 + scripts/rollup/validate/eslintrc.esm.js | 1 + 20 files changed, 405 insertions(+), 7 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2c9ad7a4c9..cd2489589e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -468,6 +468,7 @@ module.exports = { files: ['packages/react-server-dom-webpack/**/*.js'], globals: { __webpack_chunk_load__: 'readonly', + __webpack_get_script_filename__: 'readonly', __webpack_require__: 'readonly', }, }, diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index c4d47bfecc..2eef555d36 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -55,6 +55,7 @@ import { resolveServerReference, preloadModule, requireModule, + getModuleDebugInfo, dispatchHint, readPartialStringChunk, readFinalStringChunk, @@ -790,8 +791,14 @@ function resolveModuleChunk( resolvedChunk.status = RESOLVED_MODULE; resolvedChunk.value = value; if (__DEV__) { - // We don't expect to have any debug info for this row. - resolvedChunk._debugInfo = null; + const debugInfo = getModuleDebugInfo(value); + if (debugInfo !== null && resolvedChunk._debugInfo != null) { + // Add to the live set if it was already initialized. + // $FlowFixMe[method-unbinding] + resolvedChunk._debugInfo.push.apply(resolvedChunk._debugInfo, debugInfo); + } else { + resolvedChunk._debugInfo = debugInfo; + } } if (resolveListeners !== null) { initializeModuleChunk(resolvedChunk); @@ -3977,7 +3984,11 @@ function flushComponentPerformance( // Track the root most component of the result for deduping logging. result.component = componentInfo; isLastComponent = false; - } else if (candidateInfo.awaited) { + } else if ( + candidateInfo.awaited && + // Skip awaits on client resources since they didn't block the server component. + candidateInfo.awaited.env != null + ) { if (endTime > childrenEndTime) { childrenEndTime = endTime; } @@ -4059,7 +4070,11 @@ function flushComponentPerformance( // Track the root most component of the result for deduping logging. result.component = componentInfo; isLastComponent = false; - } else if (candidateInfo.awaited) { + } else if ( + candidateInfo.awaited && + // Skip awaits on client resources since they didn't block the server component. + candidateInfo.awaited.env != null + ) { // If we don't have an end time for an await, that means we aborted. const asyncInfo: ReactAsyncInfo = candidateInfo; const env = response._rootEnvironmentName; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.custom.js b/packages/react-client/src/forks/ReactFlightClientConfig.custom.js index 2f204fd51b..a6c0c933d2 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.custom.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.custom.js @@ -35,6 +35,7 @@ export const resolveClientReference = $$$config.resolveClientReference; export const resolveServerReference = $$$config.resolveServerReference; export const preloadModule = $$$config.preloadModule; export const requireModule = $$$config.requireModule; +export const getModuleDebugInfo = $$$config.getModuleDebugInfo; export const dispatchHint = $$$config.dispatchHint; export const prepareDestinationForModule = $$$config.prepareDestinationForModule; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js index 75c942966b..24caf0df88 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js @@ -24,5 +24,6 @@ export const resolveClientReference: any = null; export const resolveServerReference: any = null; export const preloadModule: any = null; export const requireModule: any = null; +export const getModuleDebugInfo: any = null; export const prepareDestinationForModule: any = null; export const usedWithSSR = true; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js index 5be648ff0a..0f7381fc5a 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js @@ -24,6 +24,7 @@ export const resolveClientReference: any = null; export const resolveServerReference: any = null; export const preloadModule: any = null; export const requireModule: any = null; +export const getModuleDebugInfo: any = null; export const dispatchHint: any = null; export const prepareDestinationForModule: any = null; export const usedWithSSR = true; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.markup.js b/packages/react-client/src/forks/ReactFlightClientConfig.markup.js index b0b2f198fd..fcd6724504 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.markup.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.markup.js @@ -62,6 +62,12 @@ export function requireModule(metadata: ClientReference): T { ); } +export function getModuleDebugInfo(metadata: ClientReference): null { + throw new Error( + 'renderToHTML should not have emitted Client References. This is a bug in React.', + ); +} + export const usedWithSSR = true; type HintCode = string; diff --git a/packages/react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM.js b/packages/react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM.js index 6a74e63854..ebd2812983 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM.js @@ -11,7 +11,11 @@ import type { Thenable, FulfilledThenable, RejectedThenable, + ReactDebugInfo, + ReactIOInfo, + ReactAsyncInfo, } from 'shared/ReactTypes'; + import type {ModuleLoading} from 'react-client/src/ReactFlightClientConfig'; export type ServerConsumerModuleMap = string; // Module root path @@ -118,3 +122,93 @@ export function requireModule(metadata: ClientReference): T { } return moduleExports[metadata.name]; } + +// We cache ReactIOInfo across requests so that inner refreshes can dedupe with outer. +const moduleIOInfoCache: Map = __DEV__ + ? new Map() + : (null: any); + +export function getModuleDebugInfo( + metadata: ClientReference, +): null | ReactDebugInfo { + if (!__DEV__) { + return null; + } + const filename = metadata.specifier; + let ioInfo = moduleIOInfoCache.get(filename); + if (ioInfo === undefined) { + let href; + try { + // $FlowFixMe + href = new URL(filename, document.baseURI).href; + } catch (_) { + href = filename; + } + let start = -1; + let end = -1; + let byteSize = 0; + // $FlowFixMe[method-unbinding] + if (typeof performance.getEntriesByType === 'function') { + // We may be able to collect the start and end time of this resource from Performance Observer. + const resourceEntries = performance.getEntriesByType('resource'); + for (let i = 0; i < resourceEntries.length; i++) { + const resourceEntry = resourceEntries[i]; + if (resourceEntry.name === href) { + start = resourceEntry.startTime; + end = start + resourceEntry.duration; + // $FlowFixMe[prop-missing] + byteSize = (resourceEntry.transferSize: any) || 0; + } + } + } + const value = Promise.resolve(href); + // $FlowFixMe + value.status = 'fulfilled'; + // Is there some more useful representation for the chunk? + // $FlowFixMe + value.value = href; + // Create a fake stack frame that points to the beginning of the chunk. This is + // probably not source mapped so will link to the compiled source rather than + // any individual file that goes into the chunks. + const fakeStack = new Error('react-stack-top-frame'); + if (fakeStack.stack.startsWith('Error: react-stack-top-frame')) { + // Looks like V8 + fakeStack.stack = + 'Error: react-stack-top-frame\n' + + // Add two frames since we always trim one off the top. + ' at Client Component Bundle (' + + href + + ':1:1)\n' + + ' at Client Component Bundle (' + + href + + ':1:1)'; + } else { + // Looks like Firefox or Safari. + // Add two frames since we always trim one off the top. + fakeStack.stack = + 'Client Component Bundle@' + + href + + ':1:1\n' + + 'Client Component Bundle@' + + href + + ':1:1'; + } + ioInfo = ({ + name: 'script', + start: start, + end: end, + value: value, + debugStack: fakeStack, + }: ReactIOInfo); + if (byteSize > 0) { + // $FlowFixMe[cannot-write] + ioInfo.byteSize = byteSize; + } + moduleIOInfoCache.set(filename, ioInfo); + } + // We could dedupe the async info too but conceptually each request is its own await. + const asyncInfo: ReactAsyncInfo = { + awaited: ioInfo, + }; + return [asyncInfo]; +} diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel.js b/packages/react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel.js index 1d380a3059..6c652c93c2 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel.js @@ -7,7 +7,7 @@ * @flow */ -import type {Thenable} from 'shared/ReactTypes'; +import type {Thenable, ReactDebugInfo} from 'shared/ReactTypes'; import type {ImportMetadata} from '../shared/ReactFlightImportMetadata'; @@ -80,3 +80,10 @@ export function requireModule(metadata: ClientReference): T { const moduleExports = parcelRequire(metadata[ID]); return moduleExports[metadata[NAME]]; } + +export function getModuleDebugInfo( + metadata: ClientReference, +): null | ReactDebugInfo { + // TODO + return null; +} diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js index fa20f032e0..ba1c220fa4 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js @@ -11,6 +11,7 @@ import type { Thenable, FulfilledThenable, RejectedThenable, + ReactDebugInfo, } from 'shared/ReactTypes'; import type { @@ -28,7 +29,10 @@ import { import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig'; -import {loadChunk} from 'react-client/src/ReactFlightClientConfig'; +import { + loadChunk, + addChunkDebugInfo, +} from 'react-client/src/ReactFlightClientConfig'; export type ServerConsumerModuleMap = null | { [clientId: string]: { @@ -231,3 +235,19 @@ export function requireModule(metadata: ClientReference): T { } return moduleExports[metadata[NAME]]; } + +export function getModuleDebugInfo( + metadata: ClientReference, +): null | ReactDebugInfo { + if (!__DEV__) { + return null; + } + const chunks = metadata[CHUNKS]; + const debugInfo: ReactDebugInfo = []; + let i = 0; + while (i < chunks.length) { + const chunkFilename = chunks[i++]; + addChunkDebugInfo(debugInfo, chunkFilename); + } + return debugInfo; +} diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser.js b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser.js index 59aab436cb..b00fe3cb06 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser.js @@ -7,6 +7,102 @@ * @flow */ +import type { + ReactDebugInfo, + ReactIOInfo, + ReactAsyncInfo, +} from 'shared/ReactTypes'; + export function loadChunk(filename: string): Promise { return __turbopack_load_by_url__(filename); } + +// We cache ReactIOInfo across requests so that inner refreshes can dedupe with outer. +const chunkIOInfoCache: Map = __DEV__ + ? new Map() + : (null: any); + +export function addChunkDebugInfo( + target: ReactDebugInfo, + filename: string, +): void { + if (!__DEV__) { + return; + } + let ioInfo = chunkIOInfoCache.get(filename); + if (ioInfo === undefined) { + let href; + try { + // $FlowFixMe + href = new URL(filename, document.baseURI).href; + } catch (_) { + href = filename; + } + let start = -1; + let end = -1; + let byteSize = 0; + // $FlowFixMe[method-unbinding] + if (typeof performance.getEntriesByType === 'function') { + // We may be able to collect the start and end time of this resource from Performance Observer. + const resourceEntries = performance.getEntriesByType('resource'); + for (let i = 0; i < resourceEntries.length; i++) { + const resourceEntry = resourceEntries[i]; + if (resourceEntry.name === href) { + start = resourceEntry.startTime; + end = start + resourceEntry.duration; + // $FlowFixMe[prop-missing] + byteSize = (resourceEntry.transferSize: any) || 0; + } + } + } + const value = Promise.resolve(href); + // $FlowFixMe + value.status = 'fulfilled'; + // Is there some more useful representation for the chunk? + // $FlowFixMe + value.value = href; + // Create a fake stack frame that points to the beginning of the chunk. This is + // probably not source mapped so will link to the compiled source rather than + // any individual file that goes into the chunks. + const fakeStack = new Error('react-stack-top-frame'); + if (fakeStack.stack.startsWith('Error: react-stack-top-frame')) { + // Looks like V8 + fakeStack.stack = + 'Error: react-stack-top-frame\n' + + // Add two frames since we always trim one off the top. + ' at Client Component Bundle (' + + href + + ':1:1)\n' + + ' at Client Component Bundle (' + + href + + ':1:1)'; + } else { + // Looks like Firefox or Safari. + // Add two frames since we always trim one off the top. + fakeStack.stack = + 'Client Component Bundle@' + + href + + ':1:1\n' + + 'Client Component Bundle@' + + href + + ':1:1'; + } + ioInfo = ({ + name: 'script', + start: start, + end: end, + value: value, + debugStack: fakeStack, + }: ReactIOInfo); + if (byteSize > 0) { + // $FlowFixMe[cannot-write] + ioInfo.byteSize = byteSize; + } + chunkIOInfoCache.set(filename, ioInfo); + } + // We could dedupe the async info too but conceptually each request is its own await. + const asyncInfo: ReactAsyncInfo = { + awaited: ioInfo, + }; + target.push(asyncInfo); +} diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer.js b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer.js index 59aab436cb..600f80c6fc 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer.js @@ -7,6 +7,16 @@ * @flow */ +import type {ReactDebugInfo} from 'shared/ReactTypes'; + export function loadChunk(filename: string): Promise { return __turbopack_load_by_url__(filename); } + +export function addChunkDebugInfo( + target: ReactDebugInfo, + filename: string, +): void { + // We don't emit any debug info on the server since we assume the loading + // of the bundle is insignificant on the server. +} diff --git a/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js b/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js index 654bcdc9b6..5574b069a4 100644 --- a/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js +++ b/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js @@ -27,6 +27,9 @@ global.__webpack_require__ = function (id) { } return webpackClientModules[id] || webpackServerModules[id]; }; +global.__webpack_get_script_filename__ = function (id) { + return id; +}; const previousCompile = Module.prototype._compile; diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode.js b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode.js index e560275cd1..de38569e52 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode.js @@ -160,3 +160,9 @@ export function requireModule(metadata: ClientReference): T { } return moduleExports[metadata.name]; } + +export function getModuleDebugInfo(metadata: ClientReference): null { + // We don't emit any debug info on the server since we assume the loading + // of the bundle is insignificant on the server. + return null; +} diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js index a43c26ac82..550e10eb00 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js @@ -11,6 +11,7 @@ import type { Thenable, FulfilledThenable, RejectedThenable, + ReactDebugInfo, } from 'shared/ReactTypes'; import type { @@ -28,7 +29,10 @@ import { import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig'; -import {loadChunk} from 'react-client/src/ReactFlightClientConfig'; +import { + loadChunk, + addChunkDebugInfo, +} from 'react-client/src/ReactFlightClientConfig'; export type ServerConsumerModuleMap = null | { [clientId: string]: { @@ -251,3 +255,20 @@ export function requireModule(metadata: ClientReference): T { } return moduleExports[metadata[NAME]]; } + +export function getModuleDebugInfo( + metadata: ClientReference, +): null | ReactDebugInfo { + if (!__DEV__) { + return null; + } + const chunks = metadata[CHUNKS]; + const debugInfo: ReactDebugInfo = []; + let i = 0; + while (i < chunks.length) { + const chunkId = chunks[i++]; + const chunkFilename = chunks[i++]; + addChunkDebugInfo(debugInfo, chunkId, chunkFilename); + } + return debugInfo; +} diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackBrowser.js b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackBrowser.js index 48779fb1e6..7f49e9fd15 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackBrowser.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackBrowser.js @@ -7,6 +7,12 @@ * @flow */ +import type { + ReactDebugInfo, + ReactIOInfo, + ReactAsyncInfo, +} from 'shared/ReactTypes'; + const chunkMap: Map = new Map(); /** @@ -26,3 +32,98 @@ export function loadChunk(chunkId: string, filename: string): Promise { chunkMap.set(chunkId, filename); return __webpack_chunk_load__(chunkId); } + +// We cache ReactIOInfo across requests so that inner refreshes can dedupe with outer. +const chunkIOInfoCache: Map = __DEV__ + ? new Map() + : (null: any); + +export function addChunkDebugInfo( + target: ReactDebugInfo, + chunkId: string, + filename: string, +): void { + if (!__DEV__) { + return; + } + let ioInfo = chunkIOInfoCache.get(chunkId); + if (ioInfo === undefined) { + const scriptFilename = __webpack_get_script_filename__(chunkId); + let href; + try { + // $FlowFixMe + href = new URL(scriptFilename, document.baseURI).href; + } catch (_) { + href = scriptFilename; + } + let start = -1; + let end = -1; + let byteSize = 0; + // $FlowFixMe[method-unbinding] + if (typeof performance.getEntriesByType === 'function') { + // We may be able to collect the start and end time of this resource from Performance Observer. + const resourceEntries = performance.getEntriesByType('resource'); + for (let i = 0; i < resourceEntries.length; i++) { + const resourceEntry = resourceEntries[i]; + if (resourceEntry.name === href) { + start = resourceEntry.startTime; + end = start + resourceEntry.duration; + // $FlowFixMe[prop-missing] + byteSize = (resourceEntry.transferSize: any) || 0; + } + } + } + const value = Promise.resolve(href); + // $FlowFixMe + value.status = 'fulfilled'; + // $FlowFixMe + value.value = { + chunkId: chunkId, + href: href, + // Is there some more useful representation for the chunk? + }; + // Create a fake stack frame that points to the beginning of the chunk. This is + // probably not source mapped so will link to the compiled source rather than + // any individual file that goes into the chunks. + const fakeStack = new Error('react-stack-top-frame'); + if (fakeStack.stack.startsWith('Error: react-stack-top-frame')) { + // Looks like V8 + fakeStack.stack = + 'Error: react-stack-top-frame\n' + + // Add two frames since we always trim one off the top. + ' at Client Component Bundle (' + + href + + ':1:1)\n' + + ' at Client Component Bundle (' + + href + + ':1:1)'; + } else { + // Looks like Firefox or Safari. + // Add two frames since we always trim one off the top. + fakeStack.stack = + 'Client Component Bundle@' + + href + + ':1:1\n' + + 'Client Component Bundle@' + + href + + ':1:1'; + } + ioInfo = ({ + name: 'script', + start: start, + end: end, + value: value, + debugStack: fakeStack, + }: ReactIOInfo); + if (byteSize > 0) { + // $FlowFixMe[cannot-write] + ioInfo.byteSize = byteSize; + } + chunkIOInfoCache.set(chunkId, ioInfo); + } + // We could dedupe the async info too but conceptually each request is its own await. + const asyncInfo: ReactAsyncInfo = { + awaited: ioInfo, + }; + target.push(asyncInfo); +} diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer.js b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer.js index 8eeb39a24a..7dcbdf3fb2 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer.js @@ -7,6 +7,17 @@ * @flow */ +import type {ReactDebugInfo} from 'shared/ReactTypes'; + export function loadChunk(chunkId: string, filename: string): Promise { return __webpack_chunk_load__(chunkId); } + +export function addChunkDebugInfo( + target: ReactDebugInfo, + chunkId: string, + filename: string, +): void { + // We don't emit any debug info on the server since we assume the loading + // of the bundle is insignificant on the server. +} diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index f52e6fd428..823defbd8d 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -146,6 +146,7 @@ declare module 'EventListener' { } declare function __webpack_chunk_load__(id: string): Promise; +declare function __webpack_get_script_filename__(id: string): string; declare const __webpack_require__: ((id: string) => any) & { u: string => string, }; diff --git a/scripts/rollup/validate/eslintrc.cjs.js b/scripts/rollup/validate/eslintrc.cjs.js index e4cf904e59..57b9043c9b 100644 --- a/scripts/rollup/validate/eslintrc.cjs.js +++ b/scripts/rollup/validate/eslintrc.cjs.js @@ -62,6 +62,7 @@ module.exports = { // Flight Webpack __webpack_chunk_load__: 'readonly', + __webpack_get_script_filename__: 'readonly', __webpack_require__: 'readonly', // Flight Turbopack diff --git a/scripts/rollup/validate/eslintrc.cjs2015.js b/scripts/rollup/validate/eslintrc.cjs2015.js index 4cda9b06a6..c9757ecd87 100644 --- a/scripts/rollup/validate/eslintrc.cjs2015.js +++ b/scripts/rollup/validate/eslintrc.cjs2015.js @@ -59,6 +59,7 @@ module.exports = { // Flight Webpack __webpack_chunk_load__: 'readonly', + __webpack_get_script_filename__: 'readonly', __webpack_require__: 'readonly', // Flight Turbopack diff --git a/scripts/rollup/validate/eslintrc.esm.js b/scripts/rollup/validate/eslintrc.esm.js index 98fdc56c48..2659200531 100644 --- a/scripts/rollup/validate/eslintrc.esm.js +++ b/scripts/rollup/validate/eslintrc.esm.js @@ -62,6 +62,7 @@ module.exports = { // Flight Webpack __webpack_chunk_load__: 'readonly', + __webpack_get_script_filename__: 'readonly', __webpack_require__: 'readonly', // Flight Turbopack