From 849217d72127dd4c8a5931d758d86a8e0e3a96ce Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 7 Jun 2024 09:24:18 -0400 Subject: [PATCH] Apply Replay changes --- .../react-devtools-extensions/src/backend.js | 38 +- .../react-devtools-extensions/src/utils.js | 24 ++ .../webpack.backend.js | 4 +- .../webpack.config.js | 16 +- .../src/backend/agent.js | 94 ++--- .../src/backend/index.js | 5 +- .../src/backend/legacy/renderer.js | 1 + .../src/backend/renderer.js | 279 +++++++++++---- .../src/backend/types.js | 11 + .../src/devtools/constants.js | 327 +----------------- .../src/devtools/store.js | 19 +- packages/react-devtools-shared/src/utils.js | 67 ++-- 12 files changed, 357 insertions(+), 528 deletions(-) diff --git a/packages/react-devtools-extensions/src/backend.js b/packages/react-devtools-extensions/src/backend.js index c7947da96310f..a506c590d2f04 100644 --- a/packages/react-devtools-extensions/src/backend.js +++ b/packages/react-devtools-extensions/src/backend.js @@ -12,22 +12,56 @@ import type {DevToolsHook} from 'react-devtools-shared/src/backend/types'; import Agent from 'react-devtools-shared/src/backend/agent'; import Bridge from 'react-devtools-shared/src/bridge'; import {initBackend} from 'react-devtools-shared/src/backend'; -import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor'; import {COMPACT_VERSION_NAME} from './utils'; +window.logMessage('RDT initializing'); setup(window.__REACT_DEVTOOLS_GLOBAL_HOOK__); +window.logMessage('RDT setup complete'); function setup(hook: ?DevToolsHook) { if (hook == null) { return; } + const bridge = new Bridge({ + listen(fn) { + const listener = event => { + if ( + event.source !== window || + !event.data || + event.data.source !== 'react-devtools-content-script' || + !event.data.payload + ) { + return; + } + fn(event.data.payload); + }; + window.addEventListener('message', listener); + return () => { + window.removeEventListener('message', listener); + }; + }, + send(event, payload, transferable) { + window.postMessage( + { + source: 'react-devtools-bridge', + payload: {event, payload}, + }, + '*', + transferable, + ); + }, + }); + + const agent = new Agent(bridge); + initBackend(hook, agent, window); + hook.backends.set(COMPACT_VERSION_NAME, { Agent, Bridge, initBackend, - setupNativeStyleEditor, + setupNativeStyleEditor: () => {}, }); hook.emit('devtools-backend-installed', COMPACT_VERSION_NAME); diff --git a/packages/react-devtools-extensions/src/utils.js b/packages/react-devtools-extensions/src/utils.js index 3219b58ebc9c1..889af3df43454 100644 --- a/packages/react-devtools-extensions/src/utils.js +++ b/packages/react-devtools-extensions/src/utils.js @@ -2,6 +2,29 @@ import type {BrowserTheme} from 'react-devtools-shared/src/devtools/views/DevTools'; +// REPLAY Our RDT integration is only for Chrome currently +export const IS_EDGE = false; // navigator.userAgent.indexOf('Edg') >= 0; +export const IS_FIREFOX = false; // navigator.userAgent.indexOf('Firefox') >= 0; +export const IS_CHROME = true; //IS_EDGE === false && IS_FIREFOX === false; + +export type BrowserName = 'Chrome' | 'Firefox' | 'Edge'; + +/* +export function getBrowserName(): BrowserName { + if (IS_EDGE) { + return 'Edge'; + } + if (IS_FIREFOX) { + return 'Firefox'; + } + if (IS_CHROME) { + return 'Chrome'; + } + throw new Error( + 'Expected browser name to be one of Chrome, Edge or Firefox.', + ); +} + export function getBrowserTheme(): BrowserTheme { if (__IS_CHROME__) { // chrome.devtools.panels added in Chrome 18. @@ -20,6 +43,7 @@ export function getBrowserTheme(): BrowserTheme { } } } +*/ export const COMPACT_VERSION_NAME = 'compact'; export const EXTENSION_CONTAINED_VERSIONS = [COMPACT_VERSION_NAME]; diff --git a/packages/react-devtools-extensions/webpack.backend.js b/packages/react-devtools-extensions/webpack.backend.js index a8069184a4aaf..6db3a32dd87ca 100644 --- a/packages/react-devtools-extensions/webpack.backend.js +++ b/packages/react-devtools-extensions/webpack.backend.js @@ -34,7 +34,7 @@ const featureFlagTarget = process.env.FEATURE_FLAG_TARGET || 'extension-oss'; module.exports = { mode: __DEV__ ? 'development' : 'production', - devtool: false, + devtool: __DEV__ ? 'cheap-module-source-map' : 'source-map', entry: { backend: './src/backend.js', }, @@ -56,7 +56,7 @@ module.exports = { }, }, optimization: { - minimize: false, + minimize: true, }, plugins: [ new Webpack.ProvidePlugin({ diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index aef6e93742b03..ef6b02cc5a563 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -48,14 +48,14 @@ module.exports = { mode: __DEV__ ? 'development' : 'production', devtool: false, entry: { - background: './src/background/index.js', - backendManager: './src/contentScripts/backendManager.js', - fileFetcher: './src/contentScripts/fileFetcher.js', - main: './src/main/index.js', - panel: './src/panel.js', - proxy: './src/contentScripts/proxy.js', - prepareInjection: './src/contentScripts/prepareInjection.js', - renderer: './src/contentScripts/renderer.js', + // background: './src/background/index.js', + // backendManager: './src/contentScripts/backendManager.js', + // fileFetcher: './src/contentScripts/fileFetcher.js', + // main: './src/main/index.js', + // panel: './src/panel.js', + // proxy: './src/contentScripts/proxy.js', + // prepareInjection: './src/contentScripts/prepareInjection.js', + // renderer: './src/contentScripts/renderer.js', installHook: './src/contentScripts/installHook.js', }, output: { diff --git a/packages/react-devtools-shared/src/backend/agent.js b/packages/react-devtools-shared/src/backend/agent.js index 54df48bfe87fd..6383f022624ca 100644 --- a/packages/react-devtools-shared/src/backend/agent.js +++ b/packages/react-devtools-shared/src/backend/agent.js @@ -8,23 +8,7 @@ */ import EventEmitter from '../events'; -import throttle from 'lodash.throttle'; -import { - SESSION_STORAGE_LAST_SELECTION_KEY, - SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, - __DEBUG__, -} from '../constants'; -import { - sessionStorageGetItem, - sessionStorageRemoveItem, - sessionStorageSetItem, -} from 'react-devtools-shared/src/storage'; -import setupHighlighter from './views/Highlighter'; -import { - initialize as setupTraceUpdates, - toggleEnabled as setTraceUpdatesEnabled, -} from './views/TraceUpdates'; +import {__DEBUG__} from '../constants'; import {patch as patchConsole} from './console'; import {currentBridgeProtocol} from 'react-devtools-shared/src/bridge'; @@ -165,26 +149,6 @@ export default class Agent extends EventEmitter<{ constructor(bridge: BackendBridge) { super(); - if ( - sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' - ) { - this._recordChangeDescriptions = - sessionStorageGetItem( - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, - ) === 'true'; - this._isProfiling = true; - - sessionStorageRemoveItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY); - sessionStorageRemoveItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY); - } - - const persistedSelectionString = sessionStorageGetItem( - SESSION_STORAGE_LAST_SELECTION_KEY, - ); - if (persistedSelectionString != null) { - this._persistedSelection = JSON.parse(persistedSelectionString); - } - this._bridge = bridge; bridge.addListener('clearErrorsAndWarnings', this.clearErrorsAndWarnings); @@ -202,7 +166,6 @@ export default class Agent extends EventEmitter<{ bridge.addListener('overrideError', this.overrideError); bridge.addListener('overrideSuspense', this.overrideSuspense); bridge.addListener('overrideValueAtPath', this.overrideValueAtPath); - bridge.addListener('reloadAndProfile', this.reloadAndProfile); bridge.addListener('renamePath', this.renamePath); bridge.addListener('setTraceUpdatesEnabled', this.setTraceUpdatesEnabled); bridge.addListener('startProfiling', this.startProfiling); @@ -244,15 +207,29 @@ export default class Agent extends EventEmitter<{ // Notify the frontend if the backend supports the Storage API (e.g. localStorage). // If not, features like reload-and-profile will not work correctly and must be disabled. let isBackendStorageAPISupported = false; - try { - localStorage.getItem('test'); - isBackendStorageAPISupported = true; - } catch (error) {} + bridge.send('isBackendStorageAPISupported', isBackendStorageAPISupported); bridge.send('isSynchronousXHRSupported', isSynchronousXHRSupported()); - setupHighlighter(bridge, this); - setupTraceUpdates(this); + window.__RECORD_REPLAY_REACT_DEVTOOLS_SEND_MESSAGE__ = ( + inEvent, + inData, + ) => { + let rv; + this._bridge = { + send(event, data) { + rv = {event, data}; + }, + }; + try { + this[inEvent](inData); + } catch (err) { + window.logMessage( + `Error executing bridge message '${inEvent}': ${err}, ${err.stack}`, + ); + } + return rv; + }; } get rendererInterfaces(): {[key: RendererID]: RendererInterface, ...} { @@ -429,7 +406,6 @@ export default class Agent extends EventEmitter<{ this._persistedSelection = null; this._persistedSelectionMatch = null; renderer.setTrackedPath(null); - this._throttledPersistSelection(rendererID, id); } // TODO: If there was a way to change the selected DOM element @@ -582,12 +558,6 @@ export default class Agent extends EventEmitter<{ reloadAndProfile: (recordChangeDescriptions: boolean) => void = recordChangeDescriptions => { - sessionStorageSetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, 'true'); - sessionStorageSetItem( - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, - recordChangeDescriptions ? 'true' : 'false', - ); - // This code path should only be hit if the shell has explicitly told the Store that it supports profiling. // In that case, the shell must also listen for this specific message to know when it needs to reload the app. // The agent can't do this in a way that is renderer agnostic. @@ -640,10 +610,9 @@ export default class Agent extends EventEmitter<{ setTraceUpdatesEnabled: (traceUpdatesEnabled: boolean) => void = traceUpdatesEnabled => { + traceUpdatesEnabled = false; this._traceUpdatesEnabled = traceUpdatesEnabled; - setTraceUpdatesEnabled(traceUpdatesEnabled); - for (const rendererID in this._rendererInterfaces) { const renderer = ((this._rendererInterfaces[ (rendererID: any) @@ -838,23 +807,4 @@ export default class Agent extends EventEmitter<{ onUnsupportedRenderer(rendererID: number) { this._bridge.send('unsupportedRendererVersion', rendererID); } - - _throttledPersistSelection: any = throttle( - (rendererID: number, id: number) => { - // This is throttled, so both renderer and selected ID - // might not be available by the time we read them. - // This is why we need the defensive checks here. - const renderer = this._rendererInterfaces[rendererID]; - const path = renderer != null ? renderer.getPathForElement(id) : null; - if (path !== null) { - sessionStorageSetItem( - SESSION_STORAGE_LAST_SELECTION_KEY, - JSON.stringify(({rendererID, path}: PersistedSelection)), - ); - } else { - sessionStorageRemoveItem(SESSION_STORAGE_LAST_SELECTION_KEY); - } - }, - 1000, - ); } diff --git a/packages/react-devtools-shared/src/backend/index.js b/packages/react-devtools-shared/src/backend/index.js index 376a23b369866..24acaa004dd02 100644 --- a/packages/react-devtools-shared/src/backend/index.js +++ b/packages/react-devtools-shared/src/backend/index.js @@ -10,7 +10,6 @@ import Agent from './agent'; import {attach} from './renderer'; -import {attach as attachLegacy} from './legacy/renderer'; import {hasAssignedBackend} from './utils'; import type {DevToolsHook, ReactRenderer, RendererInterface} from './types'; @@ -78,7 +77,9 @@ export function initBackend( rendererInterface = attach(hook, id, renderer, global); } else if (renderer.ComponentTree) { // react-dom v15 - rendererInterface = attachLegacy(hook, id, renderer, global); + // REPLAY We do not support React 15 or earlier for our RDT integration. + // Don't let this connect or save operations. + // rendererInterface = attachLegacy(hook, id, renderer, global); } else { // Older react-dom or other unsupported renderer version } diff --git a/packages/react-devtools-shared/src/backend/legacy/renderer.js b/packages/react-devtools-shared/src/backend/legacy/renderer.js index e037d45075a80..a873f9162cae6 100644 --- a/packages/react-devtools-shared/src/backend/legacy/renderer.js +++ b/packages/react-devtools-shared/src/backend/legacy/renderer.js @@ -1109,6 +1109,7 @@ export function attach( getSerializedElementValueByPath, deletePath, flushInitialOperations, + flushPendingEvents, getBestMatchForTrackedPath, getDisplayNameForFiberID, getFiberForNative, diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 59af004fb33e4..c32a968bf9875 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -32,13 +32,11 @@ import { getWrappedDisplayName, getDefaultComponentFilters, getInObject, - getUID, renamePathInObject, setInObject, utfEncodeString, filterOutLocationComponentFilters, } from 'react-devtools-shared/src/utils'; -import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; import { formatConsoleArgumentsToSingleString, gt, @@ -57,8 +55,6 @@ import { __DEBUG__, PROFILING_FLAG_BASIC_SUPPORT, PROFILING_FLAG_TIMELINE_SUPPORT, - SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, TREE_OPERATION_ADD, TREE_OPERATION_REMOVE, TREE_OPERATION_REMOVE_ROOT, @@ -67,7 +63,7 @@ import { TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS, TREE_OPERATION_UPDATE_TREE_BASE_DURATION, } from '../constants'; -import {inspectHooksOfFiber} from 'react-debug-tools'; +import {HooksNode, inspectHooksOfFiber} from 'react-debug-tools'; import { patchConsoleUsingWindowValues, registerRenderer as registerRendererWithConsole, @@ -96,11 +92,10 @@ import { MEMO_SYMBOL_STRING, SERVER_CONTEXT_SYMBOL_STRING, } from './ReactSymbols'; -import {enableStyleXFeatures} from 'react-devtools-feature-flags'; import is from 'shared/objectIs'; import hasOwnProperty from 'shared/hasOwnProperty'; -import {getStyleXData} from './StyleX/utils'; -import {createProfilingHooks} from './profilingHooks'; +// REPLAY Not doing any profiling work for the foreseeable future, disable this +// import {createProfilingHooks} from './profilingHooks'; import type {GetTimelineData, ToggleProfilingStatus} from './profilingHooks'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; @@ -143,6 +138,13 @@ type ReactPriorityLevelsType = { NoPriority: number, }; +declare var __RECORD_REPLAY_ARGUMENTS__: { + getPersistentId: (value: any) => number | null | void, + internal?: { + registerPlainObject: (value: any) => number | null | void, + }, +}; + export function getDispatcherRef(renderer: { +currentDispatcherRef?: LegacyDispatcherRef | CurrentDispatcherRef, ... @@ -180,7 +182,10 @@ const getCurrentTime = ? () => performance.now() : () => Date.now(); -export function getInternalReactConstants(version: string): { +export function getInternalReactConstants( + version: string, + getOrGenerateFiberID: (fiber: Fiber) => number, +): { getDisplayNameForFiber: getDisplayNameForFiberType, getTypeSymbol: getTypeSymbolType, ReactPriorityLevels: ReactPriorityLevelsType, @@ -468,6 +473,22 @@ export function getInternalReactConstants(version: string): { } } + function saveFiberIdToType(fiberId: number, type: Function) { + // REPLAY Save references to component functions + if (window.componentFunctionDetailsPerPoint?.has(type)) { + const functionDetails = window.componentFunctionDetailsPerPoint.get(type); + if (!functionDetails?.fiberIds.includes(fiberId)) { + functionDetails?.fiberIds?.push(fiberId); + } + } else { + const functionDetails = { + minifiedDisplayName: null, + fiberIds: [fiberId], + }; + window.componentFunctionDetailsPerPoint?.set(type, functionDetails); + } + } + // NOTICE Keep in sync with shouldFilterFiber() and other get*ForFiber methods function getDisplayNameForFiber( fiber: Fiber, @@ -475,6 +496,11 @@ export function getInternalReactConstants(version: string): { ): string | null { const {elementType, type, tag} = fiber; + const fiberId = getOrGenerateFiberID(fiber); + if (typeof fiberId === 'undefined') { + throw new Error('Could not find fiberId for fiber'); + } + let resolvedType = type; if (typeof type === 'object' && type !== null) { resolvedType = resolveFiberType(type); @@ -506,8 +532,10 @@ export function getInternalReactConstants(version: string): { case IncompleteFunctionComponent: case FunctionComponent: case IndeterminateComponent: + saveFiberIdToType(fiberId, resolvedType); return getDisplayName(resolvedType); case ForwardRef: + saveFiberIdToType(fiberId, resolvedType); return getWrappedDisplayName( elementType, resolvedType, @@ -536,6 +564,7 @@ export function getInternalReactConstants(version: string): { return 'Lazy'; case MemoComponent: case SimpleMemoComponent: + saveFiberIdToType(fiberId, resolvedType); // Display name in React does not use `Memo` as a wrapper but fallback name. return getWrappedDisplayName( elementType, @@ -563,6 +592,20 @@ export function getInternalReactConstants(version: string): { default: const typeSymbol = getTypeSymbol(type); + // REPLAY Save references to non-component types + if (window.nonComponentFiberTypesPerPoint?.has(type)) { + const fiberTypeDetails = + window.nonComponentFiberTypesPerPoint.get(type); + if (!fiberTypeDetails?.fiberIds.includes(fiberId)) { + fiberTypeDetails?.fiberIds.push(fiberId); + } + } else { + const fiberTypeDetails = { + fiberIds: [fiberId], + }; + window.nonComponentFiberTypesPerPoint?.set(type, fiberTypeDetails); + } + switch (typeSymbol) { case CONCURRENT_MODE_NUMBER: case CONCURRENT_MODE_SYMBOL_STRING: @@ -656,7 +699,7 @@ export function attach( ReactPriorityLevels, ReactTypeOfWork, StrictModeBits, - } = getInternalReactConstants(version); + } = getInternalReactConstants(version, getOrGenerateFiberID); const { CacheComponent, ClassComponent, @@ -731,6 +774,9 @@ export function attach( let getTimelineData: null | GetTimelineData = null; let toggleProfilingStatus: null | ToggleProfilingStatus = null; + + // REPLAY Not doing any profiling work for the foreseeable future, disable this + /* if (typeof injectProfilingHooks === 'function') { const response = createProfilingHooks({ getDisplayNameForFiber, @@ -748,6 +794,7 @@ export function attach( getTimelineData = response.getTimelineData; toggleProfilingStatus = response.toggleProfilingStatus; } + */ // Tracks Fibers with recently changed number of error/warning messages. // These collections store the Fiber rather than the ID, @@ -1183,25 +1230,44 @@ export function attach( // When a mount or update is in progress, this value tracks the root that is being operated on. let currentRootID: number = -1; - // Returns the unique ID for a Fiber or generates and caches a new one if the Fiber hasn't been seen before. - // Once this method has been called for a Fiber, untrackFiberID() should always be called later to avoid leaking. - function getOrGenerateFiberID(fiber: Fiber): number { - let id = null; - if (fiberToIDMap.has(fiber)) { - id = fiberToIDMap.get(fiber); - } else { - const {alternate} = fiber; - if (alternate !== null && fiberToIDMap.has(alternate)) { - id = fiberToIDMap.get(alternate); - } + function getReplayPersistentID(fiber: Fiber) { + const id = __RECORD_REPLAY_ARGUMENTS__.getPersistentId(fiber); + if (id == null) { + throw new Error('Missing persistent ID for fiber'); } + return id; + } - let didGenerateID = false; - if (id === null) { - didGenerateID = true; - id = getUID(); + // REPLAY Use persistent IDs to determine fiber IDs + function getReplayFiberID(fiber: Fiber) { + const id = getReplayPersistentID(fiber); + + let alternateFiber; + if (fiber.alternate) { + // There may be an older version of the fiber, already attached + alternateFiber = fiber.alternate; + } else if (window.unmountedFiberAlternates?.has(fiber)) { + // Or, React may have already deleted the older fiber as part of cleanup, + // but we stashed it away in the `onCommitFiberUnmount` callback before + // React actually deleted it. Look it up as provided by the eval wrapper. + alternateFiber = window.unmountedFiberAlternates.get(fiber); + } + + if (!alternateFiber) { + return id; } + // Our IDs are numeric, so older fibers will have a lower ID. + // Prefer the older fiber's ID if it exists. + const alternateId = getReplayPersistentID(alternateFiber); + return Math.min(id, alternateId); + } + + // Returns the unique ID for a Fiber or generates and caches a new one if the Fiber hasn't been seen before. + // Once this method has been called for a Fiber, untrackFiberID() should always be called later to avoid leaking. + function getOrGenerateFiberID(fiber: Fiber): number { + const id = getReplayFiberID(fiber); + // This refinement is for Flow purposes only. const refinedID = ((id: any): number); @@ -1221,17 +1287,6 @@ export function attach( } } - if (__DEBUG__) { - if (didGenerateID) { - debug( - 'getOrGenerateFiberID()', - fiber, - fiber.return, - 'Generated a new UID', - ); - } - } - return refinedID; } @@ -1983,6 +2038,10 @@ export function attach( } } + // REPLAY Save fiber IDs that were added, for use in the component commit timeline + // Already limited to fibers that should be included + window.fiberIdsAddedThisCommit?.add(id); + if (isRoot) { // Set supportsStrictMode to false for production renderer builds const isProductionBuildOfRenderer = renderer.bundleType === 0; @@ -2081,7 +2140,8 @@ export function attach( } } - const unsafeID = getFiberIDUnsafe(fiber); + const persistentID = getFiberIDUnsafe(fiber); + const unsafeID = persistentID; if (unsafeID === null) { // If we've never seen this Fiber, it might be inside of a legacy render Suspense fragment (so the store is not even aware of it). // In that case we can just ignore it or it will cause errors later on. @@ -2398,6 +2458,8 @@ export function attach( debug('updateFiberRecursively()', nextFiber, parentFiber); } + const fiberRendered = didFiberRender(prevFiber, nextFiber); + if (traceUpdatesEnabled) { const elementType = getElementTypeForFiber(nextFiber); if (traceNearestHostComponentUpdate) { @@ -2415,10 +2477,7 @@ export function attach( elementType === ElementTypeForwardRef ) { // Otherwise if this is a traced ancestor, flag for the nearest host descendant(s). - traceNearestHostComponentUpdate = didFiberRender( - prevFiber, - nextFiber, - ); + traceNearestHostComponentUpdate = fiberRendered; } } } @@ -2426,7 +2485,7 @@ export function attach( if ( mostRecentlyInspectedElement !== null && mostRecentlyInspectedElement.id === id && - didFiberRender(prevFiber, nextFiber) + fiberRenderered ) { // If this Fiber has updated, clear cached inspected data. // If it is inspected again, it may need to be re-run to obtain updated hooks values. @@ -2434,6 +2493,12 @@ export function attach( } const shouldIncludeInTree = !shouldFilterFiber(nextFiber); + + // REPLAY Save fiber IDs that were updated, for use in the component commit timeline + if (fiberRendered && shouldIncludeInTree) { + window.fiberIdsUpdatedThisCommit?.add(id); + } + const isSuspense = nextFiber.tag === SuspenseComponent; let shouldResetChildren = false; // The behavior of timed-out Suspense trees is unique. @@ -3447,12 +3512,6 @@ export function attach( stylex: null, }; - if (enableStyleXFeatures) { - if (memoizedProps != null && memoizedProps.hasOwnProperty('xstyle')) { - plugins.stylex = getStyleXData(memoizedProps.xstyle); - } - } - let source = null; if (canViewSource) { source = getSourceForFiber(fiber); @@ -3812,23 +3871,113 @@ export function attach( // Clone before cleaning so that we preserve the full data. // This will enable us to send patches without re-inspecting if hydrated paths are requested. // (Reducing how often we shallow-render is a better DX for function components that use hooks.) - const cleanedInspectedElement = {...mostRecentlyInspectedElement}; - // $FlowFixMe[prop-missing] found when upgrading Flow + const cleanedInspectedElement: InspectedElement = { + ...((mostRecentlyInspectedElement: any): InspectedElement), + }; + + // [FE-1885] Note that the internal.registerPlainObject API is only available for newer Replay Chromium builds. + const getObjectId = (object: Object) => { + if ( + __RECORD_REPLAY_ARGUMENTS__ && + __RECORD_REPLAY_ARGUMENTS__.internal && + __RECORD_REPLAY_ARGUMENTS__.internal.registerPlainObject + ) { + try { + return __RECORD_REPLAY_ARGUMENTS__.internal.registerPlainObject( + object, + ); + } catch (error) { + console.error(error); + } + } + return null; + }; + + // [FE-1885] React DevTools uses a bespoke format for inspecting props/state/hooks data; + // Replay's React DevTools fork uses the Replay Inspector (and the Replay object preview format) + // For the time being, the backend needs to support both, + // but eventually we can remove a lot of this info from the inspected element payload. + const fiber = findCurrentFiberUsingSlowPathById(id); + if (fiber == null) { + throw Error('Unexpected null'); + } + cleanedInspectedElement.contextObjectId = cleanedInspectedElement.context + ? getObjectId(cleanedInspectedElement.context) + : null; + cleanedInspectedElement.hooksObjectId = cleanedInspectedElement.hooks + ? getObjectId(cleanedInspectedElement.hooks) + : null; + cleanedInspectedElement.propsObjectId = cleanedInspectedElement.props + ? getObjectId(cleanedInspectedElement.props) + : null; + cleanedInspectedElement.stateObjectId = cleanedInspectedElement.state + ? getObjectId(cleanedInspectedElement.state) + : null; + cleanedInspectedElement.typeObjectId = cleanedInspectedElement.canViewSource + ? getObjectId(fiber.type) + : null; + + // [FE-2011] Highlight which hooks changed between renders. + const getChangedHooksHelper = (): number[] => { + const prevFiber = fiber.alternate; + if (prevFiber !== null) { + const indices = getChangedHooksIndices( + prevFiber.memoizedState, + fiber.memoizedState, + ); + return indices == null || indices.length == 0 ? [] : indices; + } + return []; + }; + + // [FE-2011] Highlight which values changed between renders. + const getChangedKeysHelper = ( + type: 'context' | 'props' | 'state', + ): string[] => { + const prevFiber = fiber.alternate; + if (prevFiber != null) { + switch (type) { + case 'context': { + const result = getContextChangedKeys(fiber); + if (Array.isArray(result)) { + return result; + } + break; + } + case 'props': { + return ( + getChangedKeys(prevFiber.memoizedProps, fiber.memoizedProps) ?? [] + ); + } + case 'state': { + return ( + getChangedKeys(prevFiber.memoizedState, fiber.memoizedState) ?? [] + ); + } + } + } + + return []; + }; + + cleanedInspectedElement.changedContextKeys = + getChangedKeysHelper('context'); + cleanedInspectedElement.changedHooksIds = getChangedHooksHelper(); + cleanedInspectedElement.changedPropsKeys = getChangedKeysHelper('props'); + cleanedInspectedElement.changedStateKeys = getChangedKeysHelper('state'); + cleanedInspectedElement.context = cleanForBridge( cleanedInspectedElement.context, createIsPathAllowed('context', null), ); - // $FlowFixMe[prop-missing] found when upgrading Flow cleanedInspectedElement.hooks = cleanForBridge( cleanedInspectedElement.hooks, createIsPathAllowed('hooks', 'hooks'), ); - // $FlowFixMe[prop-missing] found when upgrading Flow cleanedInspectedElement.props = cleanForBridge( cleanedInspectedElement.props, createIsPathAllowed('props', null), ); - // $FlowFixMe[prop-missing] found when upgrading Flow cleanedInspectedElement.state = cleanForBridge( cleanedInspectedElement.state, createIsPathAllowed('state', null), @@ -4169,7 +4318,10 @@ export function attach( }, ); - let timelineData = null; + let timelineData: null = null; + + // REPLAY Not doing any profiling work for the foreseeable future, disable this + /* if (typeof getTimelineData === 'function') { const currentTimelineData = getTimelineData(); if (currentTimelineData) { @@ -4201,6 +4353,7 @@ export function attach( }; } } + */ return { dataForRoots, @@ -4258,16 +4411,6 @@ export function attach( } } - // Automatically start profiling so that we don't miss timing info from initial "mount". - if ( - sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' - ) { - startProfiling( - sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) === - 'true', - ); - } - // React will switch between these implementations depending on whether // we have any manually suspended/errored-out Fibers or not. function shouldErrorFiberAlwaysNull() { @@ -4656,13 +4799,18 @@ export function attach( deletePath, findNativeNodesForFiberID, flushInitialOperations, + flushPendingEvents, getBestMatchForTrackedPath, + getDisplayNameForFiber, getComponentStackForFiber, getSourceForFiber, getDisplayNameForFiberID, + getElementTypeForFiber, getFiberForNative, getFiberIDForNative, + getFiberIDUnsafe, getInstanceAndStyle, + getOrGenerateFiberID, getOwnersList, getPathForElement, getProfilingData, @@ -4680,9 +4828,12 @@ export function attach( overrideValueAtPath, renamePath, renderer, + setRootPseudoKey, setTraceUpdatesEnabled, setTrackedPath, + shouldFilterFiber, startProfiling, + shouldFilterFiber, stopProfiling, storeAsGlobal, unpatchConsoleForStrictMode, diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index a64d1e5337f34..0bc8f755aee2f 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -299,6 +299,17 @@ export type InspectedElement = { // UI plugins/visualizations for the inspected element. plugins: Plugins, + + // Replay custom attributes + changedContextKeys?: string[], + changedHooksIds?: number[], + changedPropsKeys?: string[], + changedStateKeys?: string[], + contextObjectId?: number | null, + hooksObjectId?: number | null, + propsObjectId?: number | null, + stateObjectId?: number | null, + typeObjectId?: number | null, }; export const InspectElementErrorType = 'error'; diff --git a/packages/react-devtools-shared/src/devtools/constants.js b/packages/react-devtools-shared/src/devtools/constants.js index 8824003ec3742..baa41dad51a61 100644 --- a/packages/react-devtools-shared/src/devtools/constants.js +++ b/packages/react-devtools-shared/src/devtools/constants.js @@ -13,332 +13,17 @@ import type { } from './devtools/views/Settings/SettingsContext'; export const THEME_STYLES: {[style: Theme | DisplayDensity]: any, ...} = { - light: { - '--color-attribute-name': '#ef6632', - '--color-attribute-name-not-editable': '#23272f', - '--color-attribute-name-inverted': 'rgba(255, 255, 255, 0.7)', - '--color-attribute-value': '#1a1aa6', - '--color-attribute-value-inverted': '#ffffff', - '--color-attribute-editable-value': '#1a1aa6', - '--color-background': '#ffffff', - '--color-background-hover': 'rgba(0, 136, 250, 0.1)', - '--color-background-inactive': '#e5e5e5', - '--color-background-invalid': '#fff0f0', - '--color-background-selected': '#0088fa', - '--color-button-background': '#ffffff', - '--color-button-background-focus': '#ededed', - '--color-button': '#5f6673', - '--color-button-disabled': '#cfd1d5', - '--color-button-active': '#0088fa', - '--color-button-focus': '#23272f', - '--color-button-hover': '#23272f', - '--color-border': '#eeeeee', - '--color-commit-did-not-render-fill': '#cfd1d5', - '--color-commit-did-not-render-fill-text': '#000000', - '--color-commit-did-not-render-pattern': '#cfd1d5', - '--color-commit-did-not-render-pattern-text': '#333333', - '--color-commit-gradient-0': '#37afa9', - '--color-commit-gradient-1': '#63b19e', - '--color-commit-gradient-2': '#80b393', - '--color-commit-gradient-3': '#97b488', - '--color-commit-gradient-4': '#abb67d', - '--color-commit-gradient-5': '#beb771', - '--color-commit-gradient-6': '#cfb965', - '--color-commit-gradient-7': '#dfba57', - '--color-commit-gradient-8': '#efbb49', - '--color-commit-gradient-9': '#febc38', - '--color-commit-gradient-text': '#000000', - '--color-component-name': '#6a51b2', - '--color-component-name-inverted': '#ffffff', - '--color-component-badge-background': '#e6e6e6', - '--color-component-badge-background-inverted': 'rgba(255, 255, 255, 0.25)', - '--color-component-badge-count': '#777d88', - '--color-component-badge-count-inverted': 'rgba(255, 255, 255, 0.7)', - '--color-console-error-badge-text': '#ffffff', - '--color-console-error-background': '#fff0f0', - '--color-console-error-border': '#ffd6d6', - '--color-console-error-icon': '#eb3941', - '--color-console-error-text': '#fe2e31', - '--color-console-warning-badge-text': '#000000', - '--color-console-warning-background': '#fffbe5', - '--color-console-warning-border': '#fff5c1', - '--color-console-warning-icon': '#f4bd00', - '--color-console-warning-text': '#64460c', - '--color-context-background': 'rgba(0,0,0,.9)', - '--color-context-background-hover': 'rgba(255, 255, 255, 0.1)', - '--color-context-background-selected': '#178fb9', - '--color-context-border': '#3d424a', - '--color-context-text': '#ffffff', - '--color-context-text-selected': '#ffffff', - '--color-dim': '#777d88', - '--color-dimmer': '#cfd1d5', - '--color-dimmest': '#eff0f1', - '--color-error-background': 'hsl(0, 100%, 97%)', - '--color-error-border': 'hsl(0, 100%, 92%)', - '--color-error-text': '#ff0000', - '--color-expand-collapse-toggle': '#777d88', - '--color-forget-badge-background': '#2683e2', - '--color-forget-badge-background-inverted': '#1a6bbc', - '--color-forget-text': '#fff', - '--color-link': '#0000ff', - '--color-modal-background': 'rgba(255, 255, 255, 0.75)', - '--color-bridge-version-npm-background': '#eff0f1', - '--color-bridge-version-npm-text': '#000000', - '--color-bridge-version-number': '#0088fa', - '--color-primitive-hook-badge-background': '#e5e5e5', - '--color-primitive-hook-badge-text': '#5f6673', - '--color-record-active': '#fc3a4b', - '--color-record-hover': '#3578e5', - '--color-record-inactive': '#0088fa', - '--color-resize-bar': '#eeeeee', - '--color-resize-bar-active': '#dcdcdc', - '--color-resize-bar-border': '#d1d1d1', - '--color-resize-bar-dot': '#333333', - '--color-timeline-internal-module': '#d1d1d1', - '--color-timeline-internal-module-hover': '#c9c9c9', - '--color-timeline-internal-module-text': '#444', - '--color-timeline-native-event': '#ccc', - '--color-timeline-native-event-hover': '#aaa', - '--color-timeline-network-primary': '#fcf3dc', - '--color-timeline-network-primary-hover': '#f0e7d1', - '--color-timeline-network-secondary': '#efc457', - '--color-timeline-network-secondary-hover': '#e3ba52', - '--color-timeline-priority-background': '#f6f6f6', - '--color-timeline-priority-border': '#eeeeee', - '--color-timeline-user-timing': '#c9cacd', - '--color-timeline-user-timing-hover': '#93959a', - '--color-timeline-react-idle': '#d3e5f6', - '--color-timeline-react-idle-hover': '#c3d9ef', - '--color-timeline-react-render': '#9fc3f3', - '--color-timeline-react-render-hover': '#83afe9', - '--color-timeline-react-render-text': '#11365e', - '--color-timeline-react-commit': '#c88ff0', - '--color-timeline-react-commit-hover': '#b281d6', - '--color-timeline-react-commit-text': '#3e2c4a', - '--color-timeline-react-layout-effects': '#b281d6', - '--color-timeline-react-layout-effects-hover': '#9d71bd', - '--color-timeline-react-layout-effects-text': '#3e2c4a', - '--color-timeline-react-passive-effects': '#b281d6', - '--color-timeline-react-passive-effects-hover': '#9d71bd', - '--color-timeline-react-passive-effects-text': '#3e2c4a', - '--color-timeline-react-schedule': '#9fc3f3', - '--color-timeline-react-schedule-hover': '#2683E2', - '--color-timeline-react-suspense-rejected': '#f1cc14', - '--color-timeline-react-suspense-rejected-hover': '#ffdf37', - '--color-timeline-react-suspense-resolved': '#a6e59f', - '--color-timeline-react-suspense-resolved-hover': '#89d281', - '--color-timeline-react-suspense-unresolved': '#c9cacd', - '--color-timeline-react-suspense-unresolved-hover': '#93959a', - '--color-timeline-thrown-error': '#ee1638', - '--color-timeline-thrown-error-hover': '#da1030', - '--color-timeline-text-color': '#000000', - '--color-timeline-text-dim-color': '#ccc', - '--color-timeline-react-work-border': '#eeeeee', - '--color-search-match': 'yellow', - '--color-search-match-current': '#f7923b', - '--color-selected-tree-highlight-active': 'rgba(0, 136, 250, 0.1)', - '--color-selected-tree-highlight-inactive': 'rgba(0, 0, 0, 0.05)', - '--color-scroll-caret': 'rgba(150, 150, 150, 0.5)', - '--color-tab-selected-border': '#0088fa', - '--color-text': '#000000', - '--color-text-invalid': '#ff0000', - '--color-text-selected': '#ffffff', - '--color-toggle-background-invalid': '#fc3a4b', - '--color-toggle-background-on': '#0088fa', - '--color-toggle-background-off': '#cfd1d5', - '--color-toggle-text': '#ffffff', - '--color-warning-background': '#fb3655', - '--color-warning-background-hover': '#f82042', - '--color-warning-text-color': '#ffffff', - '--color-warning-text-color-inverted': '#fd4d69', - - // The styles below should be kept in sync with 'root.css' - // They are repeated there because they're used by e.g. tooltips or context menus - // which get rendered outside of the DOM subtree (where normal theme/styles are written). - '--color-scroll-thumb': '#c2c2c2', - '--color-scroll-track': '#fafafa', - '--color-tooltip-background': 'rgba(0, 0, 0, 0.9)', - '--color-tooltip-text': '#ffffff', - }, - dark: { - '--color-attribute-name': '#9d87d2', - '--color-attribute-name-not-editable': '#ededed', - '--color-attribute-name-inverted': '#282828', - '--color-attribute-value': '#cedae0', - '--color-attribute-value-inverted': '#ffffff', - '--color-attribute-editable-value': 'yellow', - '--color-background': '#282c34', - '--color-background-hover': 'rgba(255, 255, 255, 0.1)', - '--color-background-inactive': '#3d424a', - '--color-background-invalid': '#5c0000', - '--color-background-selected': '#178fb9', - '--color-button-background': '#282c34', - '--color-button-background-focus': '#3d424a', - '--color-button': '#afb3b9', - '--color-button-active': '#61dafb', - '--color-button-disabled': '#4f5766', - '--color-button-focus': '#a2e9fc', - '--color-button-hover': '#ededed', - '--color-border': '#3d424a', - '--color-commit-did-not-render-fill': '#777d88', - '--color-commit-did-not-render-fill-text': '#000000', - '--color-commit-did-not-render-pattern': '#666c77', - '--color-commit-did-not-render-pattern-text': '#ffffff', - '--color-commit-gradient-0': '#37afa9', - '--color-commit-gradient-1': '#63b19e', - '--color-commit-gradient-2': '#80b393', - '--color-commit-gradient-3': '#97b488', - '--color-commit-gradient-4': '#abb67d', - '--color-commit-gradient-5': '#beb771', - '--color-commit-gradient-6': '#cfb965', - '--color-commit-gradient-7': '#dfba57', - '--color-commit-gradient-8': '#efbb49', - '--color-commit-gradient-9': '#febc38', - '--color-commit-gradient-text': '#000000', - '--color-component-name': '#61dafb', - '--color-component-name-inverted': '#282828', - '--color-component-badge-background': '#5e6167', - '--color-component-badge-background-inverted': '#46494e', - '--color-component-badge-count': '#8f949d', - '--color-component-badge-count-inverted': 'rgba(255, 255, 255, 0.85)', - '--color-console-error-badge-text': '#000000', - '--color-console-error-background': '#290000', - '--color-console-error-border': '#5c0000', - '--color-console-error-icon': '#eb3941', - '--color-console-error-text': '#fc7f7f', - '--color-console-warning-badge-text': '#000000', - '--color-console-warning-background': '#332b00', - '--color-console-warning-border': '#665500', - '--color-console-warning-icon': '#f4bd00', - '--color-console-warning-text': '#f5f2ed', - '--color-context-background': 'rgba(255,255,255,.95)', - '--color-context-background-hover': 'rgba(0, 136, 250, 0.1)', - '--color-context-background-selected': '#0088fa', - '--color-context-border': '#eeeeee', - '--color-context-text': '#000000', - '--color-context-text-selected': '#ffffff', - '--color-dim': '#8f949d', - '--color-dimmer': '#777d88', - '--color-dimmest': '#4f5766', - '--color-error-background': '#200', - '--color-error-border': '#900', - '--color-error-text': '#f55', - '--color-expand-collapse-toggle': '#8f949d', - '--color-forget-badge-background': '#2683e2', - '--color-forget-badge-background-inverted': '#1a6bbc', - '--color-forget-text': '#fff', - '--color-link': '#61dafb', - '--color-modal-background': 'rgba(0, 0, 0, 0.75)', - '--color-bridge-version-npm-background': 'rgba(0, 0, 0, 0.25)', - '--color-bridge-version-npm-text': '#ffffff', - '--color-bridge-version-number': 'yellow', - '--color-primitive-hook-badge-background': 'rgba(0, 0, 0, 0.25)', - '--color-primitive-hook-badge-text': 'rgba(255, 255, 255, 0.7)', - '--color-record-active': '#fc3a4b', - '--color-record-hover': '#a2e9fc', - '--color-record-inactive': '#61dafb', - '--color-resize-bar': '#282c34', - '--color-resize-bar-active': '#31363f', - '--color-resize-bar-border': '#3d424a', - '--color-resize-bar-dot': '#cfd1d5', - '--color-timeline-internal-module': '#303542', - '--color-timeline-internal-module-hover': '#363b4a', - '--color-timeline-internal-module-text': '#7f8899', - '--color-timeline-native-event': '#b2b2b2', - '--color-timeline-native-event-hover': '#949494', - '--color-timeline-network-primary': '#fcf3dc', - '--color-timeline-network-primary-hover': '#e3dbc5', - '--color-timeline-network-secondary': '#efc457', - '--color-timeline-network-secondary-hover': '#d6af4d', - '--color-timeline-priority-background': '#1d2129', - '--color-timeline-priority-border': '#282c34', - '--color-timeline-user-timing': '#c9cacd', - '--color-timeline-user-timing-hover': '#93959a', - '--color-timeline-react-idle': '#3d485b', - '--color-timeline-react-idle-hover': '#465269', - '--color-timeline-react-render': '#2683E2', - '--color-timeline-react-render-hover': '#1a76d4', - '--color-timeline-react-render-text': '#11365e', - '--color-timeline-react-commit': '#731fad', - '--color-timeline-react-commit-hover': '#611b94', - '--color-timeline-react-commit-text': '#e5c1ff', - '--color-timeline-react-layout-effects': '#611b94', - '--color-timeline-react-layout-effects-hover': '#51167a', - '--color-timeline-react-layout-effects-text': '#e5c1ff', - '--color-timeline-react-passive-effects': '#611b94', - '--color-timeline-react-passive-effects-hover': '#51167a', - '--color-timeline-react-passive-effects-text': '#e5c1ff', - '--color-timeline-react-schedule': '#2683E2', - '--color-timeline-react-schedule-hover': '#1a76d4', - '--color-timeline-react-suspense-rejected': '#f1cc14', - '--color-timeline-react-suspense-rejected-hover': '#e4c00f', - '--color-timeline-react-suspense-resolved': '#a6e59f', - '--color-timeline-react-suspense-resolved-hover': '#89d281', - '--color-timeline-react-suspense-unresolved': '#c9cacd', - '--color-timeline-react-suspense-unresolved-hover': '#93959a', - '--color-timeline-thrown-error': '#fb3655', - '--color-timeline-thrown-error-hover': '#f82042', - '--color-timeline-text-color': '#282c34', - '--color-timeline-text-dim-color': '#555b66', - '--color-timeline-react-work-border': '#3d424a', - '--color-search-match': 'yellow', - '--color-search-match-current': '#f7923b', - '--color-selected-tree-highlight-active': 'rgba(23, 143, 185, 0.15)', - '--color-selected-tree-highlight-inactive': 'rgba(255, 255, 255, 0.05)', - '--color-scroll-caret': '#4f5766', - '--color-shadow': 'rgba(0, 0, 0, 0.5)', - '--color-tab-selected-border': '#178fb9', - '--color-text': '#ffffff', - '--color-text-invalid': '#ff8080', - '--color-text-selected': '#ffffff', - '--color-toggle-background-invalid': '#fc3a4b', - '--color-toggle-background-on': '#178fb9', - '--color-toggle-background-off': '#777d88', - '--color-toggle-text': '#ffffff', - '--color-warning-background': '#ee1638', - '--color-warning-background-hover': '#da1030', - '--color-warning-text-color': '#ffffff', - '--color-warning-text-color-inverted': '#ee1638', - - // The styles below should be kept in sync with 'root.css' - // They are repeated there because they're used by e.g. tooltips or context menus - // which get rendered outside of the DOM subtree (where normal theme/styles are written). - '--color-scroll-thumb': '#afb3b9', - '--color-scroll-track': '#313640', - '--color-tooltip-background': 'rgba(255, 255, 255, 0.95)', - '--color-tooltip-text': '#000000', - }, - compact: { - '--font-size-monospace-small': '9px', - '--font-size-monospace-normal': '11px', - '--font-size-monospace-large': '15px', - '--font-size-sans-small': '10px', - '--font-size-sans-normal': '12px', - '--font-size-sans-large': '14px', - '--line-height-data': '18px', - }, - comfortable: { - '--font-size-monospace-small': '10px', - '--font-size-monospace-normal': '13px', - '--font-size-monospace-large': '17px', - '--font-size-sans-small': '12px', - '--font-size-sans-normal': '14px', - '--font-size-sans-large': '16px', - '--line-height-data': '22px', - }, + light: {}, + dark: {}, + compact: {}, + comfortable: {}, }; // HACK // // Sometimes the inline target is rendered before root styles are applied, // which would result in e.g. NaN itemSize being passed to react-window list. -const COMFORTABLE_LINE_HEIGHT: number = parseInt( - THEME_STYLES.comfortable['--line-height-data'], - 10, -); -const COMPACT_LINE_HEIGHT: number = parseInt( - THEME_STYLES.compact['--line-height-data'], - 10, -); +const COMFORTABLE_LINE_HEIGHT: number = 22; +const COMPACT_LINE_HEIGHT: number = 18; export {COMFORTABLE_LINE_HEIGHT, COMPACT_LINE_HEIGHT}; diff --git a/packages/react-devtools-shared/src/devtools/store.js b/packages/react-devtools-shared/src/devtools/store.js index ef6f720346246..410f90a5008d6 100644 --- a/packages/react-devtools-shared/src/devtools/store.js +++ b/packages/react-devtools-shared/src/devtools/store.js @@ -29,7 +29,6 @@ import { utfDecodeStringWithRanges, parseElementDisplayNameFromBackend, } from '../utils'; -import {localStorageGetItem, localStorageSetItem} from '../storage'; import {__DEBUG__} from '../constants'; import {printStore} from './utils'; import ProfilerStore from './ProfilerStore'; @@ -198,13 +197,9 @@ export default class Store extends EventEmitter<{ debug('constructor', 'subscribing to Bridge'); } - this._collapseNodesByDefault = - localStorageGetItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) === - 'true'; + this._collapseNodesByDefault = false; - this._recordChangeDescriptions = - localStorageGetItem(LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) === - 'true'; + this._recordChangeDescriptions = false; this._componentFilters = getSavedComponentFilters(); @@ -327,11 +322,6 @@ export default class Store extends EventEmitter<{ set collapseNodesByDefault(value: boolean): void { this._collapseNodesByDefault = value; - localStorageSetItem( - LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY, - value ? 'true' : 'false', - ); - this.emit('collapseNodesByDefault'); } @@ -413,11 +403,6 @@ export default class Store extends EventEmitter<{ set recordChangeDescriptions(value: boolean): void { this._recordChangeDescriptions = value; - localStorageSetItem( - LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, - value ? 'true' : 'false', - ); - this.emit('recordChangeDescriptions'); } diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index 5b0903883c301..b675a23a45ec4 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -67,7 +67,6 @@ import { ElementTypeFunction, ElementTypeMemo, } from 'react-devtools-shared/src/frontend/types'; -import {localStorageGetItem, localStorageSetItem} from './storage'; import {meta} from './hydration'; import isArray from './isArray'; @@ -133,10 +132,16 @@ export function getWrappedDisplayName( wrapperName: string, fallbackName?: string, ): string { - const displayName = (outerType: any)?.displayName; - return ( - displayName || `${wrapperName}(${getDisplayName(innerType, fallbackName)})` - ); + const displayName = (outerType: any).displayName; + + let finalDisplayName = displayName; + + if (!finalDisplayName) { + const innerDisplayName = getDisplayName(innerType, fallbackName); + finalDisplayName = `${wrapperName}(${innerDisplayName})`; + } + + return finalDisplayName; } export function getDisplayName( @@ -160,6 +165,18 @@ export function getDisplayName( } cachedDisplayNames.set(type, displayName); + + if (window.componentFunctionDetailsPerPoint?.has(type)) { + const functionDetails = window.componentFunctionDetailsPerPoint.get(type); + functionDetails.minifiedDisplayName = displayName; + } else { + const functionDetails = { + minifiedDisplayName: displayName, + fiberIds: [], + }; + window.componentFunctionDetailsPerPoint?.set(type, functionDetails); + } + // window.allReactComponentDisplayNames?.set(type, displayName); return displayName; } @@ -349,26 +366,12 @@ export function getDefaultComponentFilters(): Array { } export function getSavedComponentFilters(): Array { - try { - const raw = localStorageGetItem( - LOCAL_STORAGE_COMPONENT_FILTER_PREFERENCES_KEY, - ); - if (raw != null) { - const parsedFilters: Array = JSON.parse(raw); - return filterOutLocationComponentFilters(parsedFilters); - } - } catch (error) {} return getDefaultComponentFilters(); } export function setSavedComponentFilters( componentFilters: Array, -): void { - localStorageSetItem( - LOCAL_STORAGE_COMPONENT_FILTER_PREFERENCES_KEY, - JSON.stringify(filterOutLocationComponentFilters(componentFilters)), - ); -} +): void {} // Following __debugSource removal from Fiber, the new approach for finding the source location // of a component, represented by the Fiber, is based on lazily generating and parsing component stack frames @@ -409,29 +412,19 @@ export function castBrowserTheme(v: any): ?BrowserTheme { } export function getAppendComponentStack(): boolean { - const raw = localStorageGetItem( - LOCAL_STORAGE_SHOULD_APPEND_COMPONENT_STACK_KEY, - ); - return parseBool(raw) ?? true; + return true; } export function getBreakOnConsoleErrors(): boolean { - const raw = localStorageGetItem(LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS); - return parseBool(raw) ?? false; + return false; } export function getHideConsoleLogsInStrictMode(): boolean { - const raw = localStorageGetItem( - LOCAL_STORAGE_HIDE_CONSOLE_LOGS_IN_STRICT_MODE, - ); - return parseBool(raw) ?? false; + return false; } export function getShowInlineWarningsAndErrors(): boolean { - const raw = localStorageGetItem( - LOCAL_STORAGE_SHOW_INLINE_WARNINGS_AND_ERRORS_KEY, - ); - return parseBool(raw) ?? true; + return true; } export function getDefaultOpenInEditorURL(): string { @@ -441,12 +434,6 @@ export function getDefaultOpenInEditorURL(): string { } export function getOpenInEditorURL(): string { - try { - const raw = localStorageGetItem(LOCAL_STORAGE_OPEN_IN_EDITOR_URL); - if (raw != null) { - return JSON.parse(raw); - } - } catch (error) {} return getDefaultOpenInEditorURL(); }