@samitouri / QOS-React-1 / commits / 204a551eae

Add: reload to profile for Fusebox (#31021)

## Summary Add reload to profile for Fusebox Stacked on #31048. See https://github.com/facebook/react/pull/31021/commits/6be1977112596581f7ce4cfade572f43320ab06f ## How did you test this change? Test E2E in [D63233256](https://www.internalfb.com/diff/D63233256)

Edmond Chui committed Sep 26, 2024 at 16:39 UTC 204a551eae466ab74ba23870f61dc2b5c71d5ab2
8 files changed +124 -42
packages/react-devtools-core/src/backend.js
+10 -3
@@ -26,6 +26,8 @@ import type {
26 import type {
27 DevToolsHook,
28 DevToolsHookSettings,
29 + ReloadAndProfileConfig,
30 + ReloadAndProfileConfigPersistence,
31 } from 'react-devtools-shared/src/backend/types';
32 import type {ResolveNativeStyle} from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
33
@@ -40,6 +42,7 @@ type ConnectOptions = {
42 websocket?: ?WebSocket,
43 onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
44 isReloadAndProfileSupported?: boolean,
45 + reloadAndProfileConfigPersistence?: ReloadAndProfileConfigPersistence,
46 };
47
48 let savedComponentFilters: Array<ComponentFilter> =
@@ -60,8 +63,9 @@ export function initialize(
63 maybeSettingsOrSettingsPromise?:
64 | DevToolsHookSettings
65 | Promise<DevToolsHookSettings>,
66 + reloadAndProfileConfig?: ReloadAndProfileConfig,
67 ) {
64 - installHook(window, maybeSettingsOrSettingsPromise);
68 + installHook(window, maybeSettingsOrSettingsPromise, reloadAndProfileConfig);
69 }
70
71 export function connectToDevTools(options: ?ConnectOptions) {
@@ -82,6 +86,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
86 isAppActive = () => true,
87 onSettingsUpdated,
88 isReloadAndProfileSupported = getIsReloadAndProfileSupported(),
89 + reloadAndProfileConfigPersistence,
90 } = options || {};
91
92 const protocol = useHttps ? 'wss' : 'ws';
@@ -175,7 +180,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
180
181 // TODO (npm-packages) Warn if "isBackendStorageAPISupported"
182 // $FlowFixMe[incompatible-call] found when upgrading Flow
178 - const agent = new Agent(bridge);
183 + const agent = new Agent(bridge, reloadAndProfileConfigPersistence);
184 if (onSettingsUpdated != null) {
185 agent.addListener('updateHookSettings', onSettingsUpdated);
186 }
@@ -315,6 +320,7 @@ type ConnectWithCustomMessagingOptions = {
320 resolveRNStyle?: ResolveNativeStyle,
321 onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
322 isReloadAndProfileSupported?: boolean,
323 + reloadAndProfileConfigPersistence?: ReloadAndProfileConfigPersistence,
324 };
325
326 export function connectWithCustomMessagingProtocol({
@@ -325,6 +331,7 @@ export function connectWithCustomMessagingProtocol({
331 resolveRNStyle,
332 onSettingsUpdated,
333 isReloadAndProfileSupported = getIsReloadAndProfileSupported(),
334 + reloadAndProfileConfigPersistence,
335 }: ConnectWithCustomMessagingOptions): Function {
336 const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
337 if (hook == null) {
@@ -361,7 +368,7 @@ export function connectWithCustomMessagingProtocol({
368 bridge.send('overrideComponentFilters', savedComponentFilters);
369 }
370
364 - const agent = new Agent(bridge);
371 + const agent = new Agent(bridge, reloadAndProfileConfigPersistence);
372 if (onSettingsUpdated != null) {
373 agent.addListener('updateHookSettings', onSettingsUpdated);
374 }
packages/react-devtools-fusebox/src/frontend.d.ts
+4 -1
@@ -19,9 +19,12 @@ export type Bridge = {
19 };
20 export type Store = Object;
21 export type BrowserTheme = 'dark' | 'light';
22 +export type Config = {
23 + supportsReloadAndProfile?: boolean,
24 +};
25
26 export function createBridge(wall: Wall): Bridge;
24 -export function createStore(bridge: Bridge): Store;
27 +export function createStore(bridge: Bridge, config?: Config): Store;
28
29 export type Source = {
30 sourceURL: string,
packages/react-devtools-shared/src/attachRenderer.js
+9 -1
@@ -13,6 +13,7 @@ import type {
13 DevToolsHook,
14 RendererID,
15 } from 'react-devtools-shared/src/backend/types';
16 +import type {ReloadAndProfileConfig} from './backend/types';
17
18 import {attach as attachFlight} from 'react-devtools-shared/src/backend/flight/renderer';
19 import {attach as attachFiber} from 'react-devtools-shared/src/backend/fiber/renderer';
@@ -29,6 +30,7 @@ export default function attachRenderer(
30 id: RendererID,
31 renderer: ReactRenderer,
32 global: Object,
33 + reloadAndProfileConfig: ReloadAndProfileConfig,
34 ): RendererInterface | void {
35 // only attach if the renderer is compatible with the current version of the backend
36 if (!isMatchingRender(renderer.reconcilerVersion || renderer.version)) {
@@ -48,7 +50,13 @@ export default function attachRenderer(
50 renderer.currentDispatcherRef != null
51 ) {
52 // react-reconciler v16+
51 - rendererInterface = attachFiber(hook, id, renderer, global);
53 + rendererInterface = attachFiber(
54 + hook,
55 + id,
56 + renderer,
57 + global,
58 + reloadAndProfileConfig,
59 + );
60 } else if (renderer.ComponentTree) {
61 // react-dom v15
62 rendererInterface = attachLegacy(hook, id, renderer, global);
packages/react-devtools-shared/src/backend/agent.js
+27 -25
@@ -8,17 +8,7 @@
8 */
9
10 import EventEmitter from '../events';
11 -import {
12 - SESSION_STORAGE_LAST_SELECTION_KEY,
13 - SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
14 - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
15 - __DEBUG__,
16 -} from '../constants';
17 -import {
18 - sessionStorageGetItem,
19 - sessionStorageRemoveItem,
20 - sessionStorageSetItem,
21 -} from 'react-devtools-shared/src/storage';
11 +import {SESSION_STORAGE_LAST_SELECTION_KEY, __DEBUG__} from '../constants';
12 import setupHighlighter from './views/Highlighter';
13 import {
14 initialize as setupTraceUpdates,
@@ -36,9 +26,16 @@ import type {
26 RendererID,
27 RendererInterface,
28 DevToolsHookSettings,
29 + ReloadAndProfileConfigPersistence,
30 } from './types';
31 import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types';
32 import {isReactNativeEnvironment} from './utils';
33 +import {defaultReloadAndProfileConfigPersistence} from '../utils';
34 +import {
35 + sessionStorageGetItem,
36 + sessionStorageRemoveItem,
37 + sessionStorageSetItem,
38 +} from '../storage';
39
40 const debug = (methodName: string, ...args: Array<string>) => {
41 if (__DEBUG__) {
@@ -159,21 +156,27 @@ export default class Agent extends EventEmitter<{
156 _persistedSelection: PersistedSelection | null = null;
157 _persistedSelectionMatch: PathMatch | null = null;
158 _traceUpdatesEnabled: boolean = false;
159 + _reloadAndProfileConfigPersistence: ReloadAndProfileConfigPersistence;
160
163 - constructor(bridge: BackendBridge) {
161 + constructor(
162 + bridge: BackendBridge,
163 + reloadAndProfileConfigPersistence?: ReloadAndProfileConfigPersistence = defaultReloadAndProfileConfigPersistence,
164 + ) {
165 super();
166
166 - if (
167 - sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
168 - ) {
167 + this._reloadAndProfileConfigPersistence = reloadAndProfileConfigPersistence;
168 + const {getReloadAndProfileConfig, setReloadAndProfileConfig} =
169 + reloadAndProfileConfigPersistence;
170 + const reloadAndProfileConfig = getReloadAndProfileConfig();
171 + if (reloadAndProfileConfig.shouldReloadAndProfile) {
172 this._recordChangeDescriptions =
170 - sessionStorageGetItem(
171 - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
172 - ) === 'true';
173 + reloadAndProfileConfig.recordChangeDescriptions;
174 this._isProfiling = true;
175
175 - sessionStorageRemoveItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY);
176 - sessionStorageRemoveItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY);
176 + setReloadAndProfileConfig({
177 + shouldReloadAndProfile: false,
178 + recordChangeDescriptions: false,
179 + });
180 }
181
182 const persistedSelectionString = sessionStorageGetItem(
@@ -671,11 +674,10 @@ export default class Agent extends EventEmitter<{
674
675 reloadAndProfile: (recordChangeDescriptions: boolean) => void =
676 recordChangeDescriptions => {
674 - sessionStorageSetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
675 - sessionStorageSetItem(
676 - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
677 - recordChangeDescriptions ? 'true' : 'false',
678 - );
677 + this._reloadAndProfileConfigPersistence.setReloadAndProfileConfig({
678 + shouldReloadAndProfile: true,
679 + recordChangeDescriptions,
680 + });
681
682 // This code path should only be hit if the shell has explicitly told the Store that it supports profiling.
683 // In that case, the shell must also listen for this specific message to know when it needs to reload the app.
packages/react-devtools-shared/src/backend/fiber/renderer.js
+6 -10
@@ -42,7 +42,6 @@ import {
42 utfEncodeString,
43 filterOutLocationComponentFilters,
44 } from 'react-devtools-shared/src/utils';
45 -import {sessionStorageGetItem} from 'react-devtools-shared/src/storage';
45 import {
46 formatConsoleArgumentsToSingleString,
47 gt,
@@ -61,8 +60,6 @@ import {
60 __DEBUG__,
61 PROFILING_FLAG_BASIC_SUPPORT,
62 PROFILING_FLAG_TIMELINE_SUPPORT,
64 - SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
65 - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
63 TREE_OPERATION_ADD,
64 TREE_OPERATION_REMOVE,
65 TREE_OPERATION_REORDER_CHILDREN,
@@ -106,6 +103,7 @@ import {
103 supportsOwnerStacks,
104 supportsConsoleTasks,
105 } from './DevToolsFiberComponentStack';
106 +import type {ReloadAndProfileConfig} from '../types';
107
108 // $FlowFixMe[method-unbinding]
109 const toString = Object.prototype.toString;
@@ -865,6 +863,7 @@ export function attach(
863 rendererID: number,
864 renderer: ReactRenderer,
865 global: Object,
866 + reloadAndProfileConfig: ReloadAndProfileConfig,
867 ): RendererInterface {
868 // Newer versions of the reconciler package also specific reconciler version.
869 // If that version number is present, use it.
@@ -5213,13 +5212,10 @@ export function attach(
5212 }
5213
5214 // Automatically start profiling so that we don't miss timing info from initial "mount".
5216 - if (
5217 - sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
5218 - ) {
5219 - startProfiling(
5220 - sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) ===
5221 - 'true',
5222 - );
5215 + if (reloadAndProfileConfig.shouldReloadAndProfile) {
5216 + const shouldRecordChangeDescriptions =
5217 + reloadAndProfileConfig.recordChangeDescriptions;
5218 + startProfiling(shouldRecordChangeDescriptions);
5219 }
5220
5221 function getNearestFiber(devtoolsInstance: DevToolsInstance): null | Fiber {
packages/react-devtools-shared/src/backend/types.js
+14
@@ -485,6 +485,20 @@ export type DevToolsBackend = {
485 setupNativeStyleEditor?: SetupNativeStyleEditor,
486 };
487
488 +export type ReloadAndProfileConfig = {
489 + shouldReloadAndProfile: boolean,
490 + recordChangeDescriptions: boolean,
491 +};
492 +
493 +// Linter doesn't speak Flow's `Partial` type
494 +// eslint-disable-next-line no-undef
495 +type PartialReloadAndProfileConfig = Partial<ReloadAndProfileConfig>;
496 +
497 +export type ReloadAndProfileConfigPersistence = {
498 + setReloadAndProfileConfig: (config: PartialReloadAndProfileConfig) => void,
499 + getReloadAndProfileConfig: () => ReloadAndProfileConfig,
500 +};
501 +
502 export type DevToolsHook = {
503 listeners: {[key: string]: Array<Handler>, ...},
504 rendererInterfaces: Map<RendererID, RendererInterface>,
packages/react-devtools-shared/src/hook.js
+10 -1
@@ -16,6 +16,7 @@ import type {
16 RendererInterface,
17 DevToolsBackend,
18 DevToolsHookSettings,
19 + ReloadAndProfileConfig,
20 } from './backend/types';
21
22 import {
@@ -26,6 +27,7 @@ import {
27 import attachRenderer from './attachRenderer';
28 import formatConsoleArguments from 'react-devtools-shared/src/backend/utils/formatConsoleArguments';
29 import formatWithStyles from 'react-devtools-shared/src/backend/utils/formatWithStyles';
30 +import {defaultReloadAndProfileConfigPersistence} from './utils';
31
32 // React's custom built component stack strings match "\s{4}in"
33 // Chrome's prefix matches "\s{4}at"
@@ -54,6 +56,7 @@ export function installHook(
56 maybeSettingsOrSettingsPromise?:
57 | DevToolsHookSettings
58 | Promise<DevToolsHookSettings>,
59 + reloadAndProfileConfig?: ReloadAndProfileConfig = defaultReloadAndProfileConfigPersistence.getReloadAndProfileConfig(),
60 ): DevToolsHook | null {
61 if (target.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) {
62 return null;
@@ -207,7 +210,13 @@ export function installHook(
210 reactBuildType,
211 });
212
210 - const rendererInterface = attachRenderer(hook, id, renderer, target);
213 + const rendererInterface = attachRenderer(
214 + hook,
215 + id,
216 + renderer,
217 + target,
218 + reloadAndProfileConfig,
219 + );
220 if (rendererInterface != null) {
221 hook.rendererInterfaces.set(id, rendererInterface);
222 hook.emit('renderer-attached', {id, rendererInterface});
packages/react-devtools-shared/src/utils.js
+44 -1
@@ -36,6 +36,8 @@ import {
36 TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
37 LOCAL_STORAGE_COMPONENT_FILTER_PREFERENCES_KEY,
38 LOCAL_STORAGE_OPEN_IN_EDITOR_URL,
39 + SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
40 + SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
41 } from './constants';
42 import {
43 ComponentFilterElementType,
@@ -50,7 +52,12 @@ import {
52 ElementTypeMemo,
53 ElementTypeVirtual,
54 } from 'react-devtools-shared/src/frontend/types';
53 -import {localStorageGetItem, localStorageSetItem} from './storage';
55 +import {
56 + localStorageGetItem,
57 + localStorageSetItem,
58 + sessionStorageGetItem,
59 + sessionStorageSetItem,
60 +} from './storage';
61 import {meta} from './hydration';
62 import isArray from './isArray';
63
@@ -62,6 +69,10 @@ import type {
69 } from 'react-devtools-shared/src/frontend/types';
70 import type {SerializedElement as SerializedElementBackend} from 'react-devtools-shared/src/backend/types';
71 import {isSynchronousXHRSupported} from './backend/utils';
72 +import type {
73 + ReloadAndProfileConfig,
74 + ReloadAndProfileConfigPersistence,
75 +} from './backend/types';
76
77 // $FlowFixMe[method-unbinding]
78 const hasOwnProperty = Object.prototype.hasOwnProperty;
@@ -978,3 +989,35 @@ export function getIsReloadAndProfileSupported(): boolean {
989
990 return isBackendStorageAPISupported && isSynchronousXHRSupported();
991 }
992 +
993 +export const defaultReloadAndProfileConfigPersistence: ReloadAndProfileConfigPersistence =
994 + {
995 + setReloadAndProfileConfig({
996 + shouldReloadAndProfile,
997 + recordChangeDescriptions,
998 + }): void {
999 + if (shouldReloadAndProfile != null) {
1000 + sessionStorageSetItem(
1001 + SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
1002 + shouldReloadAndProfile ? 'true' : 'false',
1003 + );
1004 + }
1005 + if (recordChangeDescriptions != null) {
1006 + sessionStorageSetItem(
1007 + SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
1008 + recordChangeDescriptions ? 'true' : 'false',
1009 + );
1010 + }
1011 + },
1012 + getReloadAndProfileConfig(): ReloadAndProfileConfig {
1013 + return {
1014 + shouldReloadAndProfile:
1015 + sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) ===
1016 + 'true',
1017 + recordChangeDescriptions:
1018 + sessionStorageGetItem(
1019 + SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
1020 + ) === 'true',
1021 + };
1022 + },
1023 + };