diff --git a/packages/react-devtools-extensions/src/main/index.js b/packages/react-devtools-extensions/src/main/index.js index 5d7bd37ee42..bce648a5dba 100644 --- a/packages/react-devtools-extensions/src/main/index.js +++ b/packages/react-devtools-extensions/src/main/index.js @@ -1,6 +1,14 @@ /* global chrome */ - +/** @flow */ + +import type {RootType} from 'react-dom/src/client/ReactDOMRoot'; +import type {FrontendBridge, Message} from 'react-devtools-shared/src/bridge'; +import type { + TabID, + ViewElementSource, +} from 'react-devtools-shared/src/devtools/views/DevTools'; import type {SourceSelection} from 'react-devtools-shared/src/devtools/views/Editor/EditorPane'; +import type {Element} from 'react-devtools-shared/src/frontend/types'; import {createElement} from 'react'; import {flushSync} from 'react-dom'; @@ -51,9 +59,9 @@ const hookNamesModuleLoaderFunction = () => resolvedParseHookNames; function createBridge() { bridge = new Bridge({ listen(fn) { - const bridgeListener = message => fn(message); + const bridgeListener = (message: Message) => fn(message); // Store the reference so that we unsubscribe from the same object. - const portOnMessage = port.onMessage; + const portOnMessage = ((port: any): ExtensionPort).onMessage; portOnMessage.addListener(bridgeListener); lastSubscribedBridgeListener = bridgeListener; @@ -71,7 +79,7 @@ function createBridge() { bridge.addListener('reloadAppForProfiling', () => { localStorageSetItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY, 'true'); - evalInInspectedWindow('reload', []); + evalInInspectedWindow('reload', [], () => {}); }); bridge.addListener( @@ -176,14 +184,20 @@ function createBridgeAndStore() { // Otherwise, the Store may miss important initial tree op codes. injectBackendManager(chrome.devtools.inspectedWindow.tabId); - const viewAttributeSourceFunction = (id, path) => { + const viewAttributeSourceFunction = ( + id: Element['id'], + path: Array, + ) => { const rendererID = store.getRendererIDForElement(id); if (rendererID != null) { viewAttributeSource(rendererID, id, path); } }; - const viewElementSourceFunction = (source, symbolicatedSource) => { + const viewElementSourceFunction: ViewElementSource = ( + source, + symbolicatedSource, + ) => { const [, sourceURL, line, column] = symbolicatedSource ? symbolicatedSource : source; @@ -198,7 +212,7 @@ function createBridgeAndStore() { root = createRoot(document.createElement('div')); - render = (overrideTab = mostRecentOverrideTab) => { + render = (overrideTab: TabID | null = mostRecentOverrideTab) => { mostRecentOverrideTab = overrideTab; root.render( @@ -227,7 +241,9 @@ function createBridgeAndStore() { }; } -function ensureInitialHTMLIsCleared(container) { +function ensureInitialHTMLIsCleared( + container: HTMLElement & {_hasInitialHTMLBeenCleared?: boolean}, +) { if (container._hasInitialHTMLBeenCleared) { return; } @@ -397,13 +413,6 @@ function createSourcesEditorPanel() { logEvent({event_name: 'selected-editor-pane'}); } }); - - createdPane.onShown.addListener(() => { - bridge.emit('extensionEditorPaneShown'); - }); - createdPane.onHidden.addListener(() => { - bridge.emit('extensionEditorPaneHidden'); - }); }); } @@ -479,10 +488,10 @@ function performInTabNavigationCleanup() { // Do not clean mostRecentOverrideTab on purpose, so we remember last opened // React DevTools tab, when user does in-tab navigation - store = null; - bridge = null; - render = null; - root = null; + store = (null: $FlowFixMe); + bridge = (null: $FlowFixMe); + render = (null: $FlowFixMe); + root = (null: $FlowFixMe); } function performFullCleanup() { @@ -504,18 +513,18 @@ function performFullCleanup() { componentsPortalContainer = null; profilerPortalContainer = null; suspensePortalContainer = null; - root = null; + root = (null: $FlowFixMe); mostRecentOverrideTab = null; - store = null; - bridge = null; - render = null; + store = (null: $FlowFixMe); + bridge = (null: $FlowFixMe); + render = (null: $FlowFixMe); port?.disconnect(); - port = null; + port = (null: $FlowFixMe); } -function connectExtensionPort() { +function connectExtensionPort(): void { if (port) { throw new Error('DevTools port was already connected'); } @@ -539,7 +548,7 @@ function connectExtensionPort() { // so, when we call `port.disconnect()` from this script, // this should not trigger this callback and port reconnection port.onDisconnect.addListener(() => { - port = null; + port = (null: $FlowFixMe); connectExtensionPort(); }); } @@ -593,9 +602,9 @@ function mountReactDevToolsWhenReactHasLoaded() { ); } -let bridge = null; +let bridge: FrontendBridge = (null: $FlowFixMe); let lastSubscribedBridgeListener = null; -let store = null; +let store: Store = (null: $FlowFixMe); let profilingData = null; @@ -610,13 +619,28 @@ let suspensePortalContainer = null; let editorPortalContainer = null; let inspectedElementPortalContainer = null; -let mostRecentOverrideTab = null; -let render = null; -let root = null; +let mostRecentOverrideTab: null | TabID = null; +let render: (overrideTab?: TabID) => void = (null: $FlowFixMe); +let root: RootType = (null: $FlowFixMe); let currentSelectedSource: null | SourceSelection = null; -let port = null; +type ExtensionEvent = { + addListener(callback: (message: Message, port: ExtensionPort) => void): void, + removeListener( + callback: (message: Message, port: ExtensionPort) => void, + ): void, +}; + +/** https://developer.chrome.com/docs/extensions/reference/api/runtime#type-Port */ +type ExtensionPort = { + onDisconnect: ExtensionEvent, + onMessage: ExtensionEvent, + postMessage(message: mixed, transferable?: Array): void, + disconnect(): void, +}; + +let port: ExtensionPort = (null: $FlowFixMe); // In case when multiple navigation events emitted in a short period of time // This debounced callback primarily used to avoid mounting React DevTools multiple times, which results @@ -649,7 +673,7 @@ connectExtensionPort(); mountReactDevToolsWhenReactHasLoaded(); -function onThemeChanged(themeName) { +function onThemeChanged() { // Rerender with the new theme render(); } diff --git a/packages/react-devtools-shared/src/Logger.js b/packages/react-devtools-shared/src/Logger.js index dd9dfb62025..0d1324d0d70 100644 --- a/packages/react-devtools-shared/src/Logger.js +++ b/packages/react-devtools-shared/src/Logger.js @@ -63,7 +63,11 @@ export type LoggerEvent = +value: any, ... }, - }; + } + | { + +event_name: 'selected-editor-pane', + } + | {+event_name: 'selected-inspected-element-pane'}; export type LogFunction = LoggerEvent => void | Promise; diff --git a/packages/react-devtools-shared/src/bridge.js b/packages/react-devtools-shared/src/bridge.js index b00867cc0cb..af98cb82989 100644 --- a/packages/react-devtools-shared/src/bridge.js +++ b/packages/react-devtools-shared/src/bridge.js @@ -74,7 +74,7 @@ export const currentBridgeProtocol: BridgeProtocol = type ElementAndRendererID = {id: number, rendererID: RendererID}; -type Message = { +export type Message = { event: string, payload: any, }; @@ -239,7 +239,7 @@ export type BackendEvents = { type StartProfilingParams = ProfilingSettings; type ReloadAndProfilingParams = ProfilingSettings; -type FrontendEvents = { +export type FrontendEvents = { clearErrorsAndWarnings: [{rendererID: RendererID}], clearErrorsForElementID: [ElementAndRendererID], clearHostInstanceHighlight: [],