From 2283d7204cfc200aa78b674d086a481c9a983007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 9 Sep 2024 15:11:57 -0400 Subject: [PATCH] [Flight] Inject Client Into DevTools (#30910) Stacked on #30906. Injects the Flight Client into the DevTools hook if it `supportsFlight`. This only injects in DEV. We could inject it in prod too but so far the only feature this exposes is only available in DEV anyway. I also only call `injectIntoDevTools` in the browser builds since we don't really support DevTools on the server anyway. The main purpose of this for now is so that DevTools can track the Server Component owner of replayed logs. This lets us add owner stacks where `console.createTask` is not natively supported (like Firefox). It also lets us associate the log with the Server Component in the Component tree #30905. --- .../react-client/src/ReactFlightClient.js | 24 +++++++++++ .../src/ReactFlightClientDevToolsHook.js | 43 +++++++++++++++++++ .../forks/ReactFlightClientConfig.custom.js | 3 ++ ...ReactFlightClientConfig.dom-browser-esm.js | 3 ++ ...lightClientConfig.dom-browser-turbopack.js | 3 ++ .../ReactFlightClientConfig.dom-browser.js | 3 ++ .../forks/ReactFlightClientConfig.dom-bun.js | 3 ++ ...ctFlightClientConfig.dom-edge-turbopack.js | 3 ++ ...eactFlightClientConfig.dom-edge-webpack.js | 3 ++ .../ReactFlightClientConfig.dom-legacy.js | 3 ++ .../ReactFlightClientConfig.dom-node-esm.js | 3 ++ ...ctFlightClientConfig.dom-node-turbopack.js | 3 ++ ...eactFlightClientConfig.dom-node-webpack.js | 2 + .../forks/ReactFlightClientConfig.dom-node.js | 3 ++ .../forks/ReactFlightClientConfig.markup.js | 3 ++ .../src/client/ReactFlightDOMClientBrowser.js | 5 +++ .../src/client/ReactFlightDOMClientBrowser.js | 5 +++ .../src/client/ReactFlightDOMClientBrowser.js | 5 +++ 18 files changed, 120 insertions(+) create mode 100644 packages/react-client/src/ReactFlightClientDevToolsHook.js diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 072df8108fccd..fe23d642a8043 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -58,6 +58,8 @@ import { createStringDecoder, prepareDestinationForModule, bindToConsole, + rendererVersion, + rendererPackageName, } from './ReactFlightClientConfig'; import {createBoundServerReference} from './ReactFlightReplyClient'; @@ -76,6 +78,10 @@ import getComponentNameFromType from 'shared/getComponentNameFromType'; import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack'; +import {injectInternals} from './ReactFlightClientDevToolsHook'; + +import ReactVersion from 'shared/ReactVersion'; + import isArray from 'shared/isArray'; import * as React from 'react'; @@ -2921,3 +2927,21 @@ export function close(response: Response): void { // ref count of pending chunks. reportGlobalError(response, new Error('Connection closed.')); } + +function getCurrentOwnerInDEV(): null | ReactComponentInfo { + return currentOwnerInDEV; +} + +export function injectIntoDevTools(): boolean { + const internals: Object = { + bundleType: __DEV__ ? 1 : 0, // Might add PROFILE later. + version: rendererVersion, + rendererPackageName: rendererPackageName, + currentDispatcherRef: ReactSharedInternals, + // Enables DevTools to detect reconciler version rather than renderer version + // which may not match for third party renderers. + reconcilerVersion: ReactVersion, + getCurrentComponentInfo: getCurrentOwnerInDEV, + }; + return injectInternals(internals); +} diff --git a/packages/react-client/src/ReactFlightClientDevToolsHook.js b/packages/react-client/src/ReactFlightClientDevToolsHook.js new file mode 100644 index 0000000000000..b8ca649d4de45 --- /dev/null +++ b/packages/react-client/src/ReactFlightClientDevToolsHook.js @@ -0,0 +1,43 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +declare const __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void; + +export function injectInternals(internals: Object): boolean { + if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { + // No DevTools + return false; + } + const hook = __REACT_DEVTOOLS_GLOBAL_HOOK__; + if (hook.isDisabled) { + // This isn't a real property on the hook, but it can be set to opt out + // of DevTools integration and associated warnings and logs. + // https://github.com/facebook/react/issues/3877 + return true; + } + if (!hook.supportsFlight) { + // DevTools exists, even though it doesn't support Flight. + return true; + } + try { + hook.inject(internals); + } catch (err) { + // Catch all errors because it is unsafe to throw during initialization. + if (__DEV__) { + console.error('React instrumentation encountered an error: %s.', err); + } + } + if (hook.checkDCE) { + // This is the real DevTools. + return true; + } else { + // This is likely a hook installed by Fast Refresh runtime. + return false; + } +} diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.custom.js b/packages/react-client/src/forks/ReactFlightClientConfig.custom.js index d9b031c4b5fe1..530d548a590d0 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.custom.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.custom.js @@ -49,3 +49,6 @@ export const readPartialStringChunk = $$$config.readPartialStringChunk; export const readFinalStringChunk = $$$config.readFinalStringChunk; export const bindToConsole = $$$config.bindToConsole; + +export const rendererVersion = $$$config.rendererVersion; +export const rendererPackageName = $$$config.rendererPackageName; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js index 55358ab05d10d..dbc89a2677d57 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-esm'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigBrowser'; export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-turbopack.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-turbopack.js index c3c511554ee6d..6a071981be9e7 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-turbopack.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-turbopack.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-turbopack'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigBrowser'; export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser.js index 41bb93db386e8..73d27adefa847 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-webpack'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigBrowser'; export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js index 0a8027e3e12aa..7b75983cdd728 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-bun'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigPlain'; export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-turbopack.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-turbopack.js index ac6d0933b7818..fbdb9fc683ac6 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-turbopack.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-turbopack.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-turbopack'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigServer'; export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-webpack.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-webpack.js index eb17f259d3e19..f328a3e2ed7b1 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-webpack.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-webpack.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-webpack'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigServer'; export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js index b992b01803260..05e937abdf82e 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'not-used'; + export * from 'react-client/src/ReactFlightClientStreamConfigWeb'; export * from 'react-client/src/ReactClientConsoleConfigBrowser'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js index 9a17b9269a948..8cb512ea44aee 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-esm'; + export * from 'react-client/src/ReactFlightClientStreamConfigNode'; export * from 'react-client/src/ReactClientConsoleConfigServer'; export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-turbopack.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-turbopack.js index f4226a93d86bc..ec97d45077b44 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-turbopack.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-turbopack.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-turbopack'; + export * from 'react-client/src/ReactFlightClientStreamConfigNode'; export * from 'react-client/src/ReactClientConsoleConfigServer'; export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-webpack.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-webpack.js index ccc12228d837f..9840d5bc911f7 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-webpack.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-webpack.js @@ -6,6 +6,8 @@ * * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-webpack'; export * from 'react-client/src/ReactFlightClientStreamConfigNode'; export * from 'react-client/src/ReactClientConsoleConfigServer'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node.js index 3425787b6434a..65e1252ee5b79 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-server-dom-webpack'; + export * from 'react-client/src/ReactFlightClientStreamConfigNode'; export * from 'react-client/src/ReactClientConsoleConfigServer'; export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerNode'; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.markup.js b/packages/react-client/src/forks/ReactFlightClientConfig.markup.js index a90acefccba39..ba86f7631ffce 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.markup.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.markup.js @@ -7,6 +7,9 @@ * @flow */ +export {default as rendererVersion} from 'shared/ReactVersion'; +export const rendererPackageName = 'react-markup'; + import type {Thenable} from 'shared/ReactTypes'; export * from 'react-markup/src/ReactMarkupLegacyClientStreamConfig.js'; diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js index 3abdac02e8c57..58a36e7023ca2 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js @@ -22,6 +22,7 @@ import { reportGlobalError, processBinaryChunk, close, + injectIntoDevTools, } from 'react-client/src/ReactFlightClient'; import { @@ -143,3 +144,7 @@ export { encodeReply, createServerReference, }; + +if (__DEV__) { + injectIntoDevTools(); +} diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js index 5a01a42417f7c..50a4a206ffb44 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js @@ -22,6 +22,7 @@ import { reportGlobalError, processBinaryChunk, close, + injectIntoDevTools, } from 'react-client/src/ReactFlightClient'; import { @@ -142,3 +143,7 @@ export { encodeReply, createServerReference, }; + +if (__DEV__) { + injectIntoDevTools(); +} diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js index 5a01a42417f7c..50a4a206ffb44 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js @@ -22,6 +22,7 @@ import { reportGlobalError, processBinaryChunk, close, + injectIntoDevTools, } from 'react-client/src/ReactFlightClient'; import { @@ -142,3 +143,7 @@ export { encodeReply, createServerReference, }; + +if (__DEV__) { + injectIntoDevTools(); +}