[DevTools] Ignore new production renderers if we already use "worse" versions of React on a page (#35994)
Sebastian "Sebbie" Silbermann committed
Mar 11, 2026 at 10:26 UTC
7b5b561bd2dda205ed8f397864379003b707d4ae
6 files changed
+89
-15
packages/react-devtools-extensions/src/background/setExtensionIconAndPopup.js
+13
-1
@@ -1,8 +1,20 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
/* global chrome */
10
11
'use strict';
12
+import type {ReactBuildType} from 'react-devtools-shared/src/backend/types';
13
5
-function setExtensionIconAndPopup(reactBuildType, tabId) {
14
+function setExtensionIconAndPopup(
15
+ reactBuildType: ReactBuildType,
16
+ tabId: number,
17
+) {
18
chrome.action.setIcon({
19
tabId,
20
path: {
packages/react-devtools-extensions/src/contentScripts/installHook.js
+2
-12
@@ -10,6 +10,7 @@ import {
10
getProfilingSettings,
11
} from 'react-devtools-shared/src/utils';
12
import {postMessage} from './messages';
13
+import {createReactRendererListener} from './reactBuildType';
14
15
let resolveHookSettingsInjection: (settings: DevToolsHookSettings) => void;
16
let resolveComponentFiltersInjection: (filters: Array<ComponentFilter>) => void;
@@ -67,17 +68,6 @@ if (!window.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) {
68
// Detect React
69
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on(
70
'renderer',
70
- function ({reactBuildType}) {
71
- window.postMessage(
72
- {
73
- source: 'react-devtools-hook',
74
- payload: {
75
- type: 'react-renderer-attached',
76
- reactBuildType,
77
- },
78
- },
79
- '*',
80
- );
81
- },
71
+ createReactRendererListener(window),
72
);
73
}
packages/react-devtools-extensions/src/contentScripts/reactBuildType.js
new
+50
@@ -0,0 +1,50 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+import type {ReactBuildType} from 'react-devtools-shared/src/backend/types';
10
+
11
+function reduceReactBuild(
12
+ currentReactBuildType: null | ReactBuildType,
13
+ nextReactBuildType: ReactBuildType,
14
+): ReactBuildType {
15
+ if (
16
+ currentReactBuildType === null ||
17
+ currentReactBuildType === 'production'
18
+ ) {
19
+ return nextReactBuildType;
20
+ }
21
+
22
+ // We only display the "worst" build type, so if we've already detected a non-production build,
23
+ // we ignore any future production builds. This way if a page has multiple renderers,
24
+ // and at least one of them is a non-production build, we'll display that instead of "production".
25
+ return nextReactBuildType === 'production'
26
+ ? currentReactBuildType
27
+ : nextReactBuildType;
28
+}
29
+
30
+export function createReactRendererListener(target: {
31
+ postMessage: Function,
32
+ ...
33
+}): ({reactBuildType: ReactBuildType}) => void {
34
+ let displayedReactBuild: null | ReactBuildType = null;
35
+
36
+ return function ({reactBuildType}) {
37
+ displayedReactBuild = reduceReactBuild(displayedReactBuild, reactBuildType);
38
+
39
+ target.postMessage(
40
+ {
41
+ source: 'react-devtools-hook',
42
+ payload: {
43
+ type: 'react-renderer-attached',
44
+ reactBuildType: displayedReactBuild,
45
+ },
46
+ },
47
+ '*',
48
+ );
49
+ };
50
+}
packages/react-devtools-shared/src/backend/types.js
+7
@@ -603,3 +603,10 @@ export type DevToolsHookSettings = {
603
export type DevToolsSettings = DevToolsHookSettings & {
604
componentFilters: Array<ComponentFilter>,
605
};
606
+
607
+export type ReactBuildType =
608
+ | 'deadcode'
609
+ | 'development'
610
+ | 'outdated'
611
+ | 'production'
612
+ | 'unminified';
packages/react-devtools-shared/src/hook.js
+3
-2
@@ -17,6 +17,7 @@ import type {
17
DevToolsBackend,
18
DevToolsHookSettings,
19
ProfilingSettings,
20
+ ReactBuildType,
21
} from './backend/types';
22
import type {ComponentFilter} from './frontend/types';
23
@@ -71,7 +72,7 @@ export function installHook(
72
return null;
73
}
74
74
- function detectReactBuildType(renderer: ReactRenderer) {
75
+ function detectReactBuildType(renderer: ReactRenderer): ReactBuildType {
76
try {
77
if (typeof renderer.version === 'string') {
78
// React DOM Fiber (16+)
@@ -211,7 +212,7 @@ export function installHook(
212
const id = ++uidCounter;
213
renderers.set(id, renderer);
214
214
- const reactBuildType = hasDetectedBadDCE
215
+ const reactBuildType: ReactBuildType = hasDetectedBadDCE
216
? 'deadcode'
217
: detectReactBuildType(renderer);
218
scripts/flow/react-devtools.js
+14
@@ -17,6 +17,16 @@ declare const __IS_CHROME__: boolean;
17
declare const __IS_EDGE__: boolean;
18
declare const __IS_NATIVE__: boolean;
19
20
+interface ExtensionAction {
21
+ /** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/action/setIcon} */
22
+ setIcon(details: {
23
+ tabId: number,
24
+ path?: string | {[iconSize: string]: string},
25
+ }): void;
26
+ /** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/action/setPopup} */
27
+ setPopup(details: {tabId: number, popup: string}): void;
28
+}
29
+
30
interface ExtensionDevtools {
31
/** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/devtools/inspectedWindow} */
32
inspectedWindow: $FlowFixMe;
@@ -73,6 +83,8 @@ interface ExtensionRuntime {
83
extensionId: string,
84
connectInfo?: {name?: string, includeTlsChannelId?: boolean},
85
): ExtensionRuntimePort;
86
+ /** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/getURL} */
87
+ getURL(path: string): string;
88
/** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage} */
89
onMessage: ExtensionEvent<
90
(
@@ -108,6 +120,8 @@ interface ExtensionTabs {
120
}
121
122
interface ExtensionAPI {
123
+ /** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/action} */
124
+ action: ExtensionAction;
125
devtools: ExtensionDevtools;
126
/** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/permissions} */
127
permissions: $FlowFixMe;