Decapitate React DevTools

Summary:
Changelog: Migrate from react-devtools-core to -react-devtools-inline

Technical design doc: https://docs.google.com/document/d/1STUSUhXzrW_KkvqSu7Ge-rxjVFF7oU3_NbwzimkO_Z4

At this point, React DevTools doe snot support globally installed DevTools. Only the bundled version. The support for the globally installed DevTools comes in the subsequent diffs along with on-the-fly transpilation.

Reviewed By: mweststrate

Differential Revision: D34926472

fbshipit-source-id: fde1d4cf386adfbf8a8581ee5a54e950d2cb34ef
This commit is contained in:
Andrey Goncharov
2022-03-31 04:01:33 -07:00
committed by Facebook GitHub Bot
parent 1f83b4b414
commit 68aec1df60
8 changed files with 372 additions and 347 deletions

View File

@@ -0,0 +1,19 @@
/**
* 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.
*
* @format
*/
export type Events = {
message: any;
connected: never;
disconnected: never;
};
export type Methods = {
message: (data: any) => Promise<void>;
globalDevTools: () => Promise<string | undefined>;
};

View File

@@ -1,14 +0,0 @@
/**
* 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.
*
* @format
*/
export function getInternalDevToolsModule<TModule>(): TModule {
throw new Error(
"Can't require internal version of React DevTools from public version of Flipper.",
);
}

View File

@@ -1,16 +0,0 @@
/**
* 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.
*
* @format
*/
declare module 'get-port' {
const getPort: (options?: {
readonly port?: number;
readonly host?: string;
}) => Promise<number>;
export default getPort;
}

View File

@@ -7,50 +7,27 @@
* @format * @format
*/ */
import type ReactDevToolsStandaloneType from 'react-devtools-core/standalone'; import {createRoot} from 'react-dom/client';
import { import {
Layout, Layout,
usePlugin, usePlugin,
DevicePluginClient, DevicePluginClient,
createState, createState,
useValue, useValue,
sleep,
Toolbar, Toolbar,
path,
getFlipperLib,
} from 'flipper-plugin'; } from 'flipper-plugin';
import React from 'react'; import React from 'react';
import getPort from 'get-port'; import {Button, message, Switch, Typography} from 'antd';
import {Button, message, Switch, Typography, Select} from 'antd'; // @ts-expect-error
import fs from 'fs'; import * as ReactDevToolsOSS from 'react-devtools-inline/frontend';
import {DevToolsEmbedder} from './DevToolsEmbedder'; import {DevToolsEmbedder} from './DevToolsEmbedder';
import {getInternalDevToolsModule} from './fb-stubs/getInternalDevToolsModule'; import {Events, Methods} from './contract';
const DEV_TOOLS_NODE_ID = 'reactdevtools-out-of-react-node'; const DEV_TOOLS_NODE_ID = 'reactdevtools-out-of-react-node';
const CONNECTED = 'DevTools connected'; const CONNECTED = 'DevTools connected';
const DEV_TOOLS_PORT = 8097; // hardcoded in RN
async function findGlobalDevTools(): Promise<string | undefined> {
try {
const {stdout: basePath} =
await getFlipperLib().remoteServerContext.childProcess.exec(
'npm root -g',
);
const devToolsPath = path.join(
basePath.trim(),
'react-devtools',
'node_modules',
'react-devtools-core',
);
await fs.promises.stat(devToolsPath);
return devToolsPath;
} catch (error) {
console.warn('Failed to find globally installed React DevTools: ' + error);
return undefined;
}
}
enum ConnectionStatus { enum ConnectionStatus {
None = 'None',
Initializing = 'Initializing...', Initializing = 'Initializing...',
WaitingForReload = 'Waiting for connection from device...', WaitingForReload = 'Waiting for connection from device...',
WaitingForMetroReload = 'Waiting for Metro to reload...', WaitingForMetroReload = 'Waiting for Metro to reload...',
@@ -58,76 +35,70 @@ enum ConnectionStatus {
Error = 'Error', Error = 'Error',
} }
type DevToolsInstanceType = 'global' | 'internal' | 'oss'; type DevToolsInstanceType = 'global' | 'oss';
type DevToolsInstance = { type DevToolsInstance = {
type: DevToolsInstanceType; type: DevToolsInstanceType;
module: ReactDevToolsStandaloneType; module: any;
}; };
export function devicePlugin(client: DevicePluginClient) { export function devicePlugin(client: DevicePluginClient<Events, Methods>) {
const metroDevice = client.device; const metroDevice = client.device;
const statusMessage = createState('initializing'); const statusMessage = createState('Empty');
const connectionStatus = createState<ConnectionStatus>( const connectionStatus = createState<ConnectionStatus>(ConnectionStatus.None);
ConnectionStatus.Initializing, const initialized = createState(false);
);
const globalDevToolsPath = createState<string>(); const globalDevToolsAvailable = createState(false);
let globalDevToolsInstance: DevToolsInstance | undefined;
const useGlobalDevTools = createState(false, { const useGlobalDevTools = createState(false, {
persist: 'useGlobalDevTools', persist: 'useGlobalDevTools',
persistToLocalStorage: true, persistToLocalStorage: true,
}); });
let devToolsInstance = getDefaultDevToolsInstance(); let devToolsInstance: DevToolsInstance | undefined;
const selectedDevToolsInstanceType = createState<DevToolsInstanceType>( const selectedDevToolsInstanceType = createState<DevToolsInstanceType>('oss');
devToolsInstance.type,
);
let startResult: {close(): void} | undefined = undefined;
let pollHandle: NodeJS.Timeout | undefined = undefined; let pollHandle: NodeJS.Timeout | undefined = undefined;
let metroReloadAttempts = 0; let metroReloadAttempts = 0;
function getGlobalDevToolsModule(): ReactDevToolsStandaloneType {
const required = (global as any).electronRequire(
globalDevToolsPath.get()!,
).default;
return required.default ?? required;
}
function getOSSDevToolsModule(): ReactDevToolsStandaloneType {
const required = require('react-devtools-core/standalone').default;
return required.default ?? required;
}
async function maybeGetInitialGlobalDevTools(): Promise<DevToolsInstance> { async function maybeGetInitialGlobalDevTools(): Promise<DevToolsInstance> {
const path = await findGlobalDevTools(); console.debug(
let instance = devToolsInstance; 'flipper-plugin-react-devtools.maybeGetInitialGlobalDevTools',
if (path) { );
globalDevToolsPath.set(path + '/standalone'); try {
console.log('Found global React DevTools: ', path); const newGlobalDevToolsSource = await client.sendToServerAddOn(
// load global devtools instance if the flag is set and 'globalDevTools',
// we're running a non-FB version of Flipper );
if (useGlobalDevTools.get() && !client.isFB) {
selectedDevToolsInstanceType.set('global');
instance = { if (newGlobalDevToolsSource) {
globalDevToolsInstance = {
type: 'global', type: 'global',
module: getGlobalDevToolsModule(), // eslint-disable-next-line no-eval
module: eval(newGlobalDevToolsSource),
}; };
globalDevToolsAvailable.set(true);
} }
} else { } catch (e) {
useGlobalDevTools.set(false); // disable in case it was enabled console.error(
} 'flipper-plugin-react-devtools.maybeGetInitialGlobalDevTools -> failed to load global devtools',
return instance; e,
);
} }
function getDefaultDevToolsInstance(): DevToolsInstance { if (useGlobalDevTools.get() && globalDevToolsInstance) {
const type = client.isFB ? 'internal' : 'oss'; console.debug(
const module = client.isFB 'flipper-plugin-react-devtools.maybeGetInitialGlobalDevTools -> using global devtools',
? getInternalDevToolsModule<ReactDevToolsStandaloneType>() );
: getOSSDevToolsModule(); return globalDevToolsInstance;
return {type, module}; }
useGlobalDevTools.set(false); // disable in case it was enabled
console.debug(
'flipper-plugin-react-devtools.maybeGetInitialGlobalDevTools -> using OSS devtools',
);
return {type: 'oss', module: ReactDevToolsOSS};
} }
function getDevToolsInstance( function getDevToolsInstance(
@@ -136,13 +107,10 @@ export function devicePlugin(client: DevicePluginClient) {
let module; let module;
switch (instanceType) { switch (instanceType) {
case 'global': case 'global':
module = getGlobalDevToolsModule(); module = globalDevToolsInstance!.module;
break;
case 'internal':
module = getInternalDevToolsModule<ReactDevToolsStandaloneType>();
break; break;
case 'oss': case 'oss':
module = getOSSDevToolsModule(); module = ReactDevToolsOSS;
break; break;
} }
return { return {
@@ -151,41 +119,19 @@ export function devicePlugin(client: DevicePluginClient) {
}; };
} }
async function setDevToolsInstance(instanceType: DevToolsInstanceType) {
selectedDevToolsInstanceType.set(instanceType);
if (instanceType === 'global') {
if (!globalDevToolsPath.get()) {
message.warn(
"No globally installed react-devtools package found. Run 'npm install -g react-devtools'.",
);
return;
}
useGlobalDevTools.set(true);
} else {
useGlobalDevTools.set(false);
}
devToolsInstance = getDevToolsInstance(instanceType);
await rebootDevTools();
}
async function toggleUseGlobalDevTools() { async function toggleUseGlobalDevTools() {
if (!globalDevToolsPath.get()) { if (!globalDevToolsInstance) {
message.warn( message.warn(
"No globally installed react-devtools package found. Run 'npm install -g react-devtools'.", "No globally installed react-devtools package found. Run 'npm install -g react-devtools'.",
); );
return; return;
} }
selectedDevToolsInstanceType.update((prev: DevToolsInstanceType) => { selectedDevToolsInstanceType.update((prev: DevToolsInstanceType) => {
if (prev === 'global') { devToolsInstance = getDevToolsInstance(
devToolsInstance = getDefaultDevToolsInstance(); prev === 'global' ? 'oss' : 'global',
);
return devToolsInstance.type; return devToolsInstance.type;
} else {
devToolsInstance = getDevToolsInstance('global');
return devToolsInstance.type;
}
}); });
useGlobalDevTools.update((v) => !v); useGlobalDevTools.update((v) => !v);
@@ -194,102 +140,94 @@ export function devicePlugin(client: DevicePluginClient) {
async function rebootDevTools() { async function rebootDevTools() {
metroReloadAttempts = 0; metroReloadAttempts = 0;
setStatus(ConnectionStatus.Initializing, 'Loading DevTools...'); setStatus(ConnectionStatus.None, 'Loading DevTools...');
// clean old instance // clean old instance
if (pollHandle) { if (pollHandle) {
clearTimeout(pollHandle); clearTimeout(pollHandle);
} }
startResult?.close();
await sleep(5000); // wait for port to close
startResult = undefined;
await bootDevTools(); await bootDevTools();
} }
async function bootDevTools() { async function bootDevTools() {
if (connectionStatus.get() !== ConnectionStatus.None) {
return;
}
if (!initialized.get()) {
console.debug(
'flipper-plugin-react-devtools -> waiting for initialization',
);
await new Promise<void>((resolve) =>
initialized.subscribe((newInitialized) => {
if (newInitialized) {
resolve();
}
}),
);
}
const devToolsNode = document.getElementById(DEV_TOOLS_NODE_ID); const devToolsNode = document.getElementById(DEV_TOOLS_NODE_ID);
if (!devToolsNode) { if (!devToolsNode) {
setStatus(ConnectionStatus.Error, 'Failed to find target DOM Node'); setStatus(ConnectionStatus.Error, 'Failed to find target DOM Node');
return; return;
} }
// React DevTools were initilized before
if (startResult) {
if (devtoolsHaveStarted()) { if (devtoolsHaveStarted()) {
setStatus(ConnectionStatus.Connected, CONNECTED); setStatus(ConnectionStatus.Connected, CONNECTED);
} else {
startPollForConnection();
}
return; return;
} }
// They're new! // They're new!
try { try {
setStatus( console.debug('flipper-plugin-react-devtools -> waiting for device');
ConnectionStatus.Initializing, setStatus(ConnectionStatus.Initializing, 'Waiting for device...');
'Waiting for port ' + DEV_TOOLS_PORT, client.onServerAddOnMessage('connected', () => {
); if (pollHandle) {
const port = await getPort({port: DEV_TOOLS_PORT}); // default port for dev tools clearTimeout(pollHandle);
if (port !== DEV_TOOLS_PORT) {
setStatus(
ConnectionStatus.Error,
`Port ${DEV_TOOLS_PORT} is already taken`,
);
return;
}
setStatus(
ConnectionStatus.Initializing,
'Starting DevTools server on ' + DEV_TOOLS_PORT,
);
startResult = devToolsInstance.module
.setContentDOMNode(devToolsNode)
.setStatusListener((message: string, status: string) => {
// TODO: since devToolsInstance is an instance, we are probably leaking memory here
if (typeof status === 'undefined') {
// Preserves old behavior in case DevTools doesn't provide status,
// which may happen if loading an older version of DevTools.
setStatus(ConnectionStatus.Initializing, message);
return;
} }
switch (status) { console.debug('flipper-plugin-react-devtools -> device found');
case 'server-connected': { setStatus(
setStatus(ConnectionStatus.Initializing, message); ConnectionStatus.Initializing,
break; 'Device found. Initializing frontend...',
} );
case 'devtools-connected': {
if (pollHandle) { const wall = {
clearTimeout(pollHandle); listen(listener: any) {
} client.onServerAddOnMessage('message', (data) => {
setStatus(ConnectionStatus.Connected, message); console.debug(
break; 'flipper-plugin-react-devtools.onServerAddOnMessage',
} data,
case 'error': { );
if (pollHandle) { listener(data);
clearTimeout(pollHandle);
}
setStatus(ConnectionStatus.Error, message);
break;
}
}
})
.startServer(DEV_TOOLS_PORT, 'localhost', undefined, {
surface: 'flipper',
}); });
setStatus(ConnectionStatus.Initializing, 'Waiting for device...'); },
send(event: any, payload: any) {
const data = {event, payload};
client.sendToServerAddOn('message', data);
},
};
const bridge = devToolsInstance!.module.createBridge(window, wall);
const store = devToolsInstance!.module.createStore(bridge);
const DevTools = devToolsInstance!.module.initialize(window, {
bridge,
store,
});
const root = createRoot(devToolsNode);
root.render(React.createElement(DevTools));
console.debug('flipper-plugin-react-devtools -> connected');
setStatus(ConnectionStatus.Connected, 'Connected');
});
startPollForConnection();
} catch (e) { } catch (e) {
console.error('Failed to initalize React DevTools' + e); console.error('Failed to initalize React DevTools' + e);
setStatus(ConnectionStatus.Error, 'Failed to initialize DevTools: ' + e); setStatus(ConnectionStatus.Error, 'Failed to initialize DevTools: ' + e);
} }
setStatus(
ConnectionStatus.Initializing,
'DevTools initialized, waiting for connection...',
);
if (devtoolsHaveStarted()) {
setStatus(ConnectionStatus.Connected, CONNECTED);
} else {
startPollForConnection();
}
} }
function setStatus(cs: ConnectionStatus, status: string) { function setStatus(cs: ConnectionStatus, status: string) {
@@ -350,17 +288,18 @@ export function devicePlugin(client: DevicePluginClient) {
); );
} }
client.onReady(async () => { client.onReady(() => {
client.onServerAddOnStart(async () => {
devToolsInstance = await maybeGetInitialGlobalDevTools(); devToolsInstance = await maybeGetInitialGlobalDevTools();
initialized.set(true);
}); });
client.onDestroy(() => {
startResult?.close();
}); });
client.onActivate(() => { client.onActivate(() => {
client.onServerAddOnStart(async () => {
bootDevTools(); bootDevTools();
}); });
});
client.onDeactivate(() => { client.onDeactivate(() => {
if (pollHandle) { if (pollHandle) {
@@ -369,84 +308,63 @@ export function devicePlugin(client: DevicePluginClient) {
}); });
return { return {
isFB: client.isFB,
devtoolsHaveStarted, devtoolsHaveStarted,
connectionStatus, connectionStatus,
statusMessage, statusMessage,
bootDevTools, bootDevTools,
rebootDevTools, rebootDevTools,
metroDevice, metroDevice,
globalDevToolsPath, globalDevToolsAvailable,
useGlobalDevTools, useGlobalDevTools,
selectedDevToolsInstanceType, selectedDevToolsInstanceType,
setDevToolsInstance,
toggleUseGlobalDevTools, toggleUseGlobalDevTools,
initialized,
}; };
} }
export function Component() { export function Component() {
const instance = usePlugin(devicePlugin);
const globalDevToolsAvailable = useValue(instance.globalDevToolsAvailable);
const connectionStatus = useValue(instance.connectionStatus);
const displayToolbar =
globalDevToolsAvailable || connectionStatus !== ConnectionStatus.Connected;
return ( return (
<Layout.Container grow> <>
<DevToolsInstanceToolbar /> <DevToolsInstanceToolbar />
<DevToolsEmbedder offset={40} nodeId={DEV_TOOLS_NODE_ID} /> <DevToolsEmbedder
</Layout.Container> offset={displayToolbar ? 40 : 0}
nodeId={DEV_TOOLS_NODE_ID}
/>
</>
); );
} }
function DevToolsInstanceToolbar() { function DevToolsInstanceToolbar() {
const instance = usePlugin(devicePlugin); const instance = usePlugin(devicePlugin);
const globalDevToolsPath = useValue(instance.globalDevToolsPath); const globalDevToolsAvailable = useValue(instance.globalDevToolsAvailable);
const connectionStatus = useValue(instance.connectionStatus); const connectionStatus = useValue(instance.connectionStatus);
const statusMessage = useValue(instance.statusMessage); const statusMessage = useValue(instance.statusMessage);
const useGlobalDevTools = useValue(instance.useGlobalDevTools); const useGlobalDevTools = useValue(instance.useGlobalDevTools);
const selectedDevToolsInstanceType = useValue( const initialized = useValue(instance.initialized);
instance.selectedDevToolsInstanceType,
);
if (!globalDevToolsPath && !instance.isFB) { const selectionControl = globalDevToolsAvailable ? (
return null;
}
let selectionControl;
if (instance.isFB) {
const devToolsInstanceOptions = [{value: 'internal'}, {value: 'oss'}];
if (globalDevToolsPath) {
devToolsInstanceOptions.push({value: 'global'});
}
selectionControl = (
<>
Select preferred DevTools version:
<Select
options={devToolsInstanceOptions}
value={selectedDevToolsInstanceType}
onChange={instance.setDevToolsInstance}
style={{width: 90}}
size="small"
/>
</>
);
} else if (globalDevToolsPath) {
selectionControl = (
<> <>
<Switch <Switch
checked={useGlobalDevTools} checked={useGlobalDevTools}
onChange={instance.toggleUseGlobalDevTools} onChange={instance.toggleUseGlobalDevTools}
size="small" size="small"
disabled={!initialized}
/> />
Use globally installed DevTools Use globally installed DevTools
</> </>
); ) : null;
} else {
throw new Error(
'Should not render Toolbar if not FB build or a global DevTools install not available.',
);
}
return ( return (
<Layout.Container grow>
<Toolbar right={selectionControl} wash> <Toolbar right={selectionControl} wash>
{connectionStatus !== ConnectionStatus.Connected ? (
<Typography.Text type="secondary">{statusMessage}</Typography.Text> <Typography.Text type="secondary">{statusMessage}</Typography.Text>
) : null}
{connectionStatus === ConnectionStatus.WaitingForReload || {connectionStatus === ConnectionStatus.WaitingForReload ||
connectionStatus === ConnectionStatus.WaitingForMetroReload || connectionStatus === ConnectionStatus.WaitingForMetroReload ||
connectionStatus === ConnectionStatus.Error ? ( connectionStatus === ConnectionStatus.Error ? (
@@ -460,5 +378,6 @@ function DevToolsInstanceToolbar() {
</Button> </Button>
) : null} ) : null}
</Toolbar> </Toolbar>
</Layout.Container>
); );
} }

View File

@@ -12,14 +12,15 @@
], ],
"main": "dist/bundle.js", "main": "dist/bundle.js",
"flipperBundlerEntry": "index.tsx", "flipperBundlerEntry": "index.tsx",
"serverAddOn": "dist/serverAddOn.js",
"flipperBundlerEntryServerAddOn": "serverAddOn.tsx",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"flipper-plugin" "flipper-plugin"
], ],
"dependencies": { "dependencies": {
"address": "^1.1.2", "react-devtools-inline": "^4.24.3",
"get-port": "^5.0.0", "ws": "^8.5.0"
"react-devtools-core": "4.24.1"
}, },
"title": "React DevTools", "title": "React DevTools",
"icon": "app-react", "icon": "app-react",

View File

@@ -1,35 +0,0 @@
/**
* 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.
*
* @format
*/
type ServerOptions = {
key?: string;
cert?: string;
};
type LoggerOptions = {
surface?: string;
};
type StatusTypes = 'server-connected' | 'devtools-connected' | 'error';
type StatusListener = (message: string, status: StatusTypes) => void;
declare module 'react-devtools-core/standalone' {
interface DevTools {
setContentDOMNode(node: HTMLElement): this;
startServer(
port?: number,
host?: string,
httpsOptions?: ServerOptions,
loggerOptions?: LoggerOptions,
): {close: () => void};
setStatusListener(listener: StatusListener): this;
}
const DevTools: DevTools;
export default DevTools;
}

View File

@@ -0,0 +1,156 @@
/**
* 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.
*
* @format
*/
import {
createControlledPromise,
FlipperServerForServerAddOn,
ServerAddOn,
} from 'flipper-plugin';
import path from 'path';
import {WebSocketServer, WebSocket} from 'ws';
import {Events, Methods} from './contract';
const DEV_TOOLS_PORT = 8097; // hardcoded in RN
async function findGlobalDevTools(
flipperServer: FlipperServerForServerAddOn,
): Promise<string | undefined> {
try {
const {stdout: basePath} = await flipperServer.exec(
'node-api-exec',
'npm root -g',
);
console.debug(
'flipper-plugin-react-devtools.findGlobalDevTools -> npm root',
basePath,
);
const devToolsPath = path.join(
basePath.trim(),
'react-devtools-inline',
'dist',
'frontend.js',
);
await flipperServer.exec('node-api-fs-stat', devToolsPath);
return devToolsPath;
} catch (error) {
console.warn('Failed to find globally installed React DevTools: ' + error);
return undefined;
}
}
const serverAddOn: ServerAddOn<Events, Methods> = async (
connection,
{flipperServer},
) => {
console.debug('flipper-plugin-react-devtools.serverAddOn -> starting');
const startServer = async () => {
console.debug('flipper-plugin-react-devtools.serverAddOn -> startServer');
const wss = new WebSocketServer({port: DEV_TOOLS_PORT});
const startedPromise = createControlledPromise<void>();
wss.on('listening', () => startedPromise.resolve());
wss.on('error', (err) => {
if (startedPromise.state === 'pending') {
startedPromise.reject(err);
return;
}
console.error('flipper-plugin-react-devtools.serverAddOn -> error', err);
});
await startedPromise.promise;
console.debug(
'flipper-plugin-react-devtools.serverAddOn -> started server',
);
wss.on('connection', (ws) => {
connection.send('connected');
console.debug(
'flipper-plugin-react-devtools.serverAddOn -> connected a client',
);
ws.on('message', (data) => {
connection.send('message', JSON.parse(data.toString()));
console.debug(
'flipper-plugin-react-devtools.serverAddOn -> client sent a message',
data.toString(),
);
});
ws.on('error', (err) => {
console.error(
'flipper-plugin-react-devtools.serverAddOn -> client error',
err,
);
});
ws.on('close', () => {
connection.send('disconnected');
console.debug(
'flipper-plugin-react-devtools.serverAddOn -> client left',
);
});
});
connection.receive('message', (data) => {
console.debug(
'flipper-plugin-react-devtools.serverAddOn -> desktop sent a message',
data,
);
wss!.clients.forEach((ws) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
});
});
return wss;
};
const wss = await startServer();
connection.receive('globalDevTools', async () => {
const globalDevToolsPath = await findGlobalDevTools(flipperServer);
if (!globalDevToolsPath) {
console.info(
'flipper-plugin-react-devtools.serverAddOn -> not found global React DevTools',
);
return;
}
console.info(
'flipper-plugin-react-devtools.serverAddOn -> found global React DevTools: ',
globalDevToolsPath,
);
// TODO: Transform ReactDevTools for browsers
/*
const globalDevToolsSource = globalDevToolsPath;
return globalDevToolsSource;
*/
return;
});
return async () => {
console.debug('flipper-plugin-react-devtools.serverAddOn -> stopping');
if (wss) {
console.debug(
'flipper-plugin-react-devtools.serverAddOn -> stopping wss',
);
await new Promise<void>((resolve, reject) =>
wss!.close((err) => (err ? reject(err) : resolve())),
);
console.debug('flipper-plugin-react-devtools.serverAddOn -> stopped wss');
}
};
};
export default serverAddOn;

View File

@@ -267,11 +267,6 @@ JSONSelect@0.2.1:
resolved "https://registry.yarnpkg.com/JSONSelect/-/JSONSelect-0.2.1.tgz#415418a526d33fe31d74b4defa3c836d485ec203" resolved "https://registry.yarnpkg.com/JSONSelect/-/JSONSelect-0.2.1.tgz#415418a526d33fe31d74b4defa3c836d485ec203"
integrity sha1-QVQYpSbTP+MddLTe+jyDbUhewgM= integrity sha1-QVQYpSbTP+MddLTe+jyDbUhewgM=
address@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
ansi-regex@^5.0.0: ansi-regex@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
@@ -880,11 +875,6 @@ get-intrinsic@^1.0.2:
has "^1.0.3" has "^1.0.3"
has-symbols "^1.0.1" has-symbols "^1.0.1"
get-port@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
get-value@^2.0.3, get-value@^2.0.6: get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -1483,13 +1473,13 @@ raf@^3.1.0, raf@^3.4.0:
dependencies: dependencies:
performance-now "^2.1.0" performance-now "^2.1.0"
react-devtools-core@4.24.1: react-devtools-inline@^4.24.3:
version "4.24.1" version "4.24.3"
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.1.tgz#d274390db86898d779b6202a318fe6422eb046bb" resolved "https://registry.yarnpkg.com/react-devtools-inline/-/react-devtools-inline-4.24.3.tgz#a60259ec397c126452b259444b39e6cc98d253d4"
integrity sha512-skar+cqSg5Oz89n4lQ/aBQS8RGj93FMufg2TrMJqE+RSUTO9nLEYawRMXXCs8PnDVRSfG5pPVU5Nt1OegNflyA== integrity sha512-A98BC0538RDCRJeM2tHFOL6xzl1DvGxEWqf0oW51NFEmRmtALyw47OtRmyR1cBA4Q7cIKKWLab/DHIVTEZssbA==
dependencies: dependencies:
shell-quote "^1.6.1" source-map-js "^0.6.2"
ws "^7" sourcemap-codec "^1.4.8"
react-is@16.10.2: react-is@16.10.2:
version "16.10.2" version "16.10.2"
@@ -1721,11 +1711,6 @@ shebang-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
shell-quote@^1.6.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==
slash@^2.0.0: slash@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
@@ -1761,6 +1746,11 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0" source-map-resolve "^0.5.0"
use "^3.1.0" use "^3.1.0"
source-map-js@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
source-map-resolve@^0.5.0: source-map-resolve@^0.5.0:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -1782,6 +1772,11 @@ source-map@^0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
sourcemap-codec@^1.4.8:
version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
split-string@^3.0.1, split-string@^3.0.2: split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@@ -1910,10 +1905,10 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^7: ws@^8.5.0:
version "7.5.6" version "8.5.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
xml-beautifier@^0.4.0: xml-beautifier@^0.4.0:
version "0.4.3" version "0.4.3"