/
ashipov
/
react
Обзор
Документация
Войти
/
ashipov
/
react
Код
Запросы
0
Задачи 2.0
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
packages/react-devtools-extensions/src/contentScripts/backendManager.js
210 строк
6 KB
Ruslan Lesiutin
refactor[react-devtools]: flatten reload and profile config (#31132)
09 окт 2024, 15:57
Не верифицирован
09 окт 2024, 15:57
bfe91fb
Код
Авторство
О чём код?
/** * 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 */ import type { DevToolsHook, ReactRenderer, } from 'react-devtools-shared/src/backend/types'; import {hasAssignedBackend} from 'react-devtools-shared/src/backend/utils'; import {COMPACT_VERSION_NAME} from 'react-devtools-extensions/src/utils'; import {getIsReloadAndProfileSupported} from 'react-devtools-shared/src/utils'; import { getIfReloadedAndProfiling, onReloadAndProfile, onReloadAndProfileFlagsReset, } from 'react-devtools-shared/src/utils'; let welcomeHasInitialized = false; const requiredBackends = new Set<string>(); function welcome(event: $FlowFixMe) { if ( event.source !== window || event.data.source !== 'react-devtools-content-script' ) { return; } // In some circumstances, this method is called more than once for a single welcome message. // The exact circumstances of this are unclear, though it seems related to 3rd party event batching code. // // Regardless, call this method multiple times can cause DevTools to add duplicate elements to the Store // (and throw an error) or worse yet, choke up entirely and freeze the browser. // // The simplest solution is to ignore the duplicate events. // To be clear, this SHOULD NOT BE NECESSARY, since we remove the event handler below. // // See https://github.com/facebook/react/issues/24162 if (welcomeHasInitialized) { console.warn( 'React DevTools detected duplicate welcome "message" events from the content script.', ); return; } welcomeHasInitialized = true; window.removeEventListener('message', welcome); setup(window.__REACT_DEVTOOLS_GLOBAL_HOOK__); } function setup(hook: ?DevToolsHook) { // this should not happen, but Chrome can be weird sometimes if (hook == null) { return; } // register renderers that have already injected themselves. hook.renderers.forEach(renderer => { registerRenderer(renderer, hook); }); // Activate and remove from required all present backends, registered within the hook hook.backends.forEach((_, backendVersion) => { requiredBackends.delete(backendVersion); activateBackend(backendVersion, hook); }); updateRequiredBackends(); // register renderers that inject themselves later. const unsubscribeRendererListener = hook.sub('renderer', ({renderer}) => { registerRenderer(renderer, hook); updateRequiredBackends(); }); // listen for backend installations. const unsubscribeBackendInstallationListener = hook.sub( 'devtools-backend-installed', version => { activateBackend(version, hook); updateRequiredBackends(); }, ); const unsubscribeShutdownListener: () => void = hook.sub('shutdown', () => { unsubscribeRendererListener(); unsubscribeBackendInstallationListener(); unsubscribeShutdownListener(); }); } function registerRenderer(renderer: ReactRenderer, hook: DevToolsHook) { let version = renderer.reconcilerVersion || renderer.version; if (!hasAssignedBackend(version)) { version = COMPACT_VERSION_NAME; } // Check if required backend is already activated, no need to require again if (!hook.backends.has(version)) { requiredBackends.add(version); } } function activateBackend(version: string, hook: DevToolsHook) { const backend = hook.backends.get(version); if (!backend) { throw new Error(`Could not find backend for version "${version}"`); } const {Agent, Bridge, initBackend, setupNativeStyleEditor} = backend; const bridge = new Bridge({ listen(fn) { const listener = (event: $FlowFixMe) => { 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: string, payload: any, transferable?: Array<any>) { window.postMessage( { source: 'react-devtools-bridge', payload: {event, payload}, }, '*', transferable, ); }, }); const agent = new Agent( bridge, getIfReloadedAndProfiling(), onReloadAndProfile, ); // Agent read flags successfully, we can count it as successful launch // Clean up flags, so that next reload won't start profiling onReloadAndProfileFlagsReset(); agent.addListener('shutdown', () => { // If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down, // and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here. hook.emit('shutdown'); delete window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__; }); initBackend(hook, agent, window, getIsReloadAndProfileSupported()); // Setup React Native style editor if a renderer like react-native-web has injected it. if (typeof setupNativeStyleEditor === 'function' && hook.resolveRNStyle) { setupNativeStyleEditor( bridge, agent, hook.resolveRNStyle, hook.nativeStyleEditorValidAttributes, ); } // Let the frontend know that the backend has attached listeners and is ready for messages. // This covers the case of syncing saved values after reloading/navigating while DevTools remain open. bridge.send('extensionBackendInitialized'); // this backend is activated requiredBackends.delete(version); } // tell the service worker which versions of backends are needed for the current page function updateRequiredBackends() { if (requiredBackends.size === 0) { return; } window.postMessage( { source: 'react-devtools-backend-manager', payload: { type: 'require-backends', versions: Array.from(requiredBackends), }, }, '*', ); } /* * Make sure this is executed only once in case Frontend is reloaded multiple times while Backend is initializing * We can't use `reactDevToolsAgent` field on a global Hook object, because it only cleaned up after both Frontend and Backend initialized */ if (!window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__) { window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__ = true; window.addEventListener('message', welcome); }