From 053e9c10cb8fae91a2baa4d9b9acd6d50f2a3e1c Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 7 Feb 2024 20:18:31 -0500 Subject: [PATCH] Add environmentName option This lets you name the server that is producing the debug info so that you can trace the origin of where that component is executing. --- .../react-client/src/ReactFlightClient.js | 2 +- .../src/__tests__/ReactFlight-test.js | 23 +++++++++++++------ .../src/ReactNoopFlightServer.js | 6 ++++- .../src/ReactFlightDOMServerNode.js | 2 ++ .../src/ReactFlightDOMServerFB.js | 2 ++ .../src/ReactFlightDOMServerBrowser.js | 2 ++ .../src/ReactFlightDOMServerEdge.js | 2 ++ .../src/ReactFlightDOMServerNode.js | 2 ++ .../src/ReactFlightDOMServerBrowser.js | 2 ++ .../src/ReactFlightDOMServerEdge.js | 2 ++ .../src/ReactFlightDOMServerNode.js | 2 ++ .../src/__tests__/ReactFlightDOMEdge-test.js | 2 +- .../react-server/src/ReactFlightServer.js | 20 ++++++++++++---- packages/react/src/ReactLazy.js | 2 +- .../react/src/__tests__/ReactFetch-test.js | 2 +- 15 files changed, 56 insertions(+), 17 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 9579c3691e52e..46e09b1cf0274 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -77,7 +77,7 @@ const INITIALIZED = 'fulfilled'; const ERRORED = 'rejected'; // Dev-only -type ReactDebugInfo = Array<{+name?: string}>; +type ReactDebugInfo = Array<{+name?: string, +env?: string}>; type PendingChunk = { status: 'pending', diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 940e298b22745..52c127397e6d8 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -187,7 +187,7 @@ describe('ReactFlight', () => { const rootModel = await ReactNoopFlightClient.read(transport); const greeting = rootModel.greeting; expect(greeting._debugInfo).toEqual( - __DEV__ ? [{name: 'Greeting'}] : undefined, + __DEV__ ? [{name: 'Greeting', env: 'server'}] : undefined, ); ReactNoop.render(greeting); }); @@ -214,7 +214,7 @@ describe('ReactFlight', () => { await act(async () => { const promise = ReactNoopFlightClient.read(transport); expect(promise._debugInfo).toEqual( - __DEV__ ? [{name: 'Greeting'}] : undefined, + __DEV__ ? [{name: 'Greeting', env: 'server'}] : undefined, ); ReactNoop.render(await promise); }); @@ -1826,9 +1826,14 @@ describe('ReactFlight', () => { return
Hello, {children}
; } - const promise = Promise.resolve(); + const promiseComponent = Promise.resolve(); - const thirdPartyTransport = ReactNoopFlightServer.render([promise, lazy]); + const thirdPartyTransport = ReactNoopFlightServer.render( + [promiseComponent, lazy], + { + environmentName: 'third-party', + }, + ); // Wait for the lazy component to initialize await 0; @@ -1840,16 +1845,20 @@ describe('ReactFlight', () => { await act(async () => { const promise = ReactNoopFlightClient.read(transport); expect(promise._debugInfo).toEqual( - __DEV__ ? [{name: 'ServerComponent'}] : undefined, + __DEV__ ? [{name: 'ServerComponent', env: 'server'}] : undefined, ); const result = await promise; const thirdPartyChildren = await result.props.children[1]; // We expect the debug info to be transferred from the inner stream to the outer. expect(thirdPartyChildren[0]._debugInfo).toEqual( - __DEV__ ? [{name: 'ThirdPartyComponent'}] : undefined, + __DEV__ + ? [{name: 'ThirdPartyComponent', env: 'third-party'}] + : undefined, ); expect(thirdPartyChildren[1]._debugInfo).toEqual( - __DEV__ ? [{name: 'ThirdPartyLazyComponent'}] : undefined, + __DEV__ + ? [{name: 'ThirdPartyLazyComponent', env: 'third-party'}] + : undefined, ); ReactNoop.render(result); }); diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 41bdcd3d6b0d4..3d46f7694c798 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -68,8 +68,10 @@ const ReactNoopFlightServer = ReactFlightServer({ }); type Options = { - onError?: (error: mixed) => void, + environmentName?: string, identifierPrefix?: string, + onError?: (error: mixed) => void, + onPostpone?: (reason: string) => void, }; function render(model: ReactClientValue, options?: Options): Destination { @@ -80,6 +82,8 @@ function render(model: ReactClientValue, options?: Options): Destination { bundlerConfig, options ? options.onError : undefined, options ? options.identifierPrefix : undefined, + options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); ReactNoopFlightServer.startWork(request); ReactNoopFlightServer.startFlowing(request, destination); diff --git a/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js index 08c2ede7f8f9c..df14751b74342 100644 --- a/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js @@ -52,6 +52,7 @@ function createDrainHandler(destination: Destination, request: Request) { } type Options = { + environmentName?: string, onError?: (error: mixed) => void, onPostpone?: (reason: string) => void, identifierPrefix?: string, @@ -73,6 +74,7 @@ function renderToPipeableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); let hasStartedFlowing = false; startWork(request); diff --git a/packages/react-server-dom-fb/src/ReactFlightDOMServerFB.js b/packages/react-server-dom-fb/src/ReactFlightDOMServerFB.js index fdfa5a008f659..5fc7f11e7cb88 100644 --- a/packages/react-server-dom-fb/src/ReactFlightDOMServerFB.js +++ b/packages/react-server-dom-fb/src/ReactFlightDOMServerFB.js @@ -50,6 +50,8 @@ function renderToDestination( model, null, options ? options.onError : undefined, + undefined, + undefined, ); startWork(request); startFlowing(request, destination); diff --git a/packages/react-server-dom-turbopack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-turbopack/src/ReactFlightDOMServerBrowser.js index ed8407b312749..da9094cc5212d 100644 --- a/packages/react-server-dom-turbopack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-turbopack/src/ReactFlightDOMServerBrowser.js @@ -34,6 +34,7 @@ export { } from './ReactFlightTurbopackReferences'; type Options = { + environmentName?: string, identifierPrefix?: string, signal?: AbortSignal, onError?: (error: mixed) => void, @@ -51,6 +52,7 @@ function renderToReadableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); if (options && options.signal) { const signal = options.signal; diff --git a/packages/react-server-dom-turbopack/src/ReactFlightDOMServerEdge.js b/packages/react-server-dom-turbopack/src/ReactFlightDOMServerEdge.js index ed8407b312749..da9094cc5212d 100644 --- a/packages/react-server-dom-turbopack/src/ReactFlightDOMServerEdge.js +++ b/packages/react-server-dom-turbopack/src/ReactFlightDOMServerEdge.js @@ -34,6 +34,7 @@ export { } from './ReactFlightTurbopackReferences'; type Options = { + environmentName?: string, identifierPrefix?: string, signal?: AbortSignal, onError?: (error: mixed) => void, @@ -51,6 +52,7 @@ function renderToReadableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); if (options && options.signal) { const signal = options.signal; diff --git a/packages/react-server-dom-turbopack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-turbopack/src/ReactFlightDOMServerNode.js index a988b79fad885..5be4c4546544a 100644 --- a/packages/react-server-dom-turbopack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-turbopack/src/ReactFlightDOMServerNode.js @@ -49,6 +49,7 @@ function createDrainHandler(destination: Destination, request: Request) { } type Options = { + environmentName?: string, onError?: (error: mixed) => void, onPostpone?: (reason: string) => void, identifierPrefix?: string, @@ -70,6 +71,7 @@ function renderToPipeableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); let hasStartedFlowing = false; startWork(request); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index 2c5c5bcebc500..2e389abd5a041 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -38,6 +38,7 @@ export { } from './ReactFlightWebpackReferences'; type Options = { + environmentName?: string, identifierPrefix?: string, signal?: AbortSignal, onError?: (error: mixed) => void, @@ -55,6 +56,7 @@ function renderToReadableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); if (options && options.signal) { const signal = options.signal; diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js index 2c5c5bcebc500..2e389abd5a041 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js @@ -38,6 +38,7 @@ export { } from './ReactFlightWebpackReferences'; type Options = { + environmentName?: string, identifierPrefix?: string, signal?: AbortSignal, onError?: (error: mixed) => void, @@ -55,6 +56,7 @@ function renderToReadableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); if (options && options.signal) { const signal = options.signal; diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index 850511ab099ef..7546e1eac6612 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -61,6 +61,7 @@ function createCancelHandler(request: Request, reason: string) { } type Options = { + environmentName?: string, onError?: (error: mixed) => void, onPostpone?: (reason: string) => void, identifierPrefix?: string, @@ -82,6 +83,7 @@ function renderToPipeableStream( options ? options.onError : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined, + options ? options.environmentName : undefined, ); let hasStartedFlowing = false; startWork(request); diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 2eaf6b30a7506..eb2298197dfbb 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -286,7 +286,7 @@ describe('ReactFlightDOMEdge', () => { , ); const serializedContent = await readResult(stream); - const expectedDebugInfoSize = __DEV__ ? 30 * 20 : 0; + const expectedDebugInfoSize = __DEV__ ? 42 * 20 : 0; expect(serializedContent.length).toBeLessThan(150 + expectedDebugInfoSize); }); diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index bb0d1e954dfcf..0350b1a6abf3a 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -108,7 +108,7 @@ import {SuspenseException, getSuspendedThenable} from './ReactFlightThenable'; initAsyncDebugInfo(); // Dev-only -type ReactDebugInfo = Array<{+name?: string}>; +type ReactDebugInfo = Array<{+name?: string, +env?: string}>; const ObjectPrototype = Object.prototype; @@ -202,6 +202,8 @@ export type Request = { taintCleanupQueue: Array, onError: (error: mixed) => ?string, onPostpone: (reason: string) => void, + // DEV-only + environmentName: string, }; const { @@ -254,6 +256,7 @@ export function createRequest( onError: void | ((error: mixed) => ?string), identifierPrefix?: string, onPostpone: void | ((reason: string) => void), + environmentName: void | string, ): Request { if ( ReactCurrentCache.current !== null && @@ -273,7 +276,7 @@ export function createRequest( TaintRegistryPendingRequests.add(cleanupQueue); } const hints = createHints(); - const request: Request = { + const request: Request = ({ status: OPEN, flushScheduled: false, fatalError: null, @@ -298,7 +301,11 @@ export function createRequest( taintCleanupQueue: cleanupQueue, onError: onError === undefined ? defaultErrorHandler : onError, onPostpone: onPostpone === undefined ? defaultPostponeHandler : onPostpone, - }; + }: any); + if (__DEV__) { + request.environmentName = + environmentName === undefined ? 'server' : environmentName; + } const rootTask = createTask(request, model, null, false, abortSet); pingedTasks.push(rootTask); return request; @@ -519,7 +526,10 @@ function renderFunctionComponent( const componentName = (Component: any).displayName || Component.name || ''; request.pendingChunks++; - emitDebugChunk(request, debugID, {name: componentName}); + emitDebugChunk(request, debugID, { + name: componentName, + env: request.environmentName, + }); } } @@ -1717,7 +1727,7 @@ function emitModelChunk(request: Request, id: number, json: string): void { function emitDebugChunk( request: Request, id: number, - debugInfo: {+name?: string}, + debugInfo: {+name?: string, +env?: string}, ): void { if (!__DEV__) { // These errors should never make it into a build so we don't need to encode them in codes.json diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index 7c219638408e6..ece18edca40a3 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -46,7 +46,7 @@ export type LazyComponent = { $$typeof: symbol | number, _payload: P, _init: (payload: P) => T, - _debugInfo?: null | Array<{+name?: string}>, + _debugInfo?: null | Array<{+name?: string, +env?: string}>, }; function lazyInitializer(payload: Payload): T { diff --git a/packages/react/src/__tests__/ReactFetch-test.js b/packages/react/src/__tests__/ReactFetch-test.js index 9bb4d89777221..f89cce08fc138 100644 --- a/packages/react/src/__tests__/ReactFetch-test.js +++ b/packages/react/src/__tests__/ReactFetch-test.js @@ -85,7 +85,7 @@ describe('ReactFetch', () => { const promise = render(Component); expect(await promise).toMatchInlineSnapshot(`"GET world []"`); expect(promise._debugInfo).toEqual( - __DEV__ ? [{name: 'Component'}] : undefined, + __DEV__ ? [{name: 'Component', env: 'server'}] : undefined, ); expect(fetchCount).toBe(1); });