Move plugins to "sonar/desktop/plugins"
Summary: Plugins moved from "sonar/desktop/src/plugins" to "sonar/desktop/plugins". Fixed all the paths after moving. New "desktop" folder structure: - `src` - Flipper desktop app JS code executing in Electron Renderer (Chrome) process. - `static` - Flipper desktop app JS code executing in Electron Main (Node.js) process. - `plugins` - Flipper desktop JS plugins. - `pkg` - Flipper packaging lib and CLI tool. - `doctor` - Flipper diagnostics lib and CLI tool. - `scripts` - Build scripts for Flipper desktop app. - `headless` - Headless version of Flipper desktop app. - `headless-tests` - Integration tests running agains Flipper headless version. Reviewed By: mweststrate Differential Revision: D20344186 fbshipit-source-id: d020da970b2ea1e001f9061a8782bfeb54e31ba0
This commit is contained in:
committed by
Facebook GitHub Bot
parent
beb5c85e69
commit
10d990c32c
16
desktop/plugins/reactdevtools/get-port.d.tsx
Normal file
16
desktop/plugins/reactdevtools/get-port.d.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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;
|
||||
}
|
||||
283
desktop/plugins/reactdevtools/index.tsx
Normal file
283
desktop/plugins/reactdevtools/index.tsx
Normal file
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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 ReactDevToolsStandalone from 'react-devtools-core/standalone';
|
||||
import {
|
||||
FlipperDevicePlugin,
|
||||
AndroidDevice,
|
||||
styled,
|
||||
View,
|
||||
MetroDevice,
|
||||
ReduxState,
|
||||
connect,
|
||||
Device,
|
||||
CenteredView,
|
||||
RoundedSection,
|
||||
Text,
|
||||
Button,
|
||||
} from 'flipper';
|
||||
import React, {useEffect} from 'react';
|
||||
import getPort from 'get-port';
|
||||
|
||||
const Container = styled.div({
|
||||
display: 'flex',
|
||||
flex: '1 1 0%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'stretch',
|
||||
height: '100%',
|
||||
});
|
||||
|
||||
const DEV_TOOLS_NODE_ID = 'reactdevtools-out-of-react-node';
|
||||
|
||||
function createDevToolsNode(): HTMLElement {
|
||||
const div = document.createElement('div');
|
||||
div.id = DEV_TOOLS_NODE_ID;
|
||||
div.style.display = 'none';
|
||||
div.style.width = '100%';
|
||||
div.style.height = '100%';
|
||||
div.style.flex = '1 1 0%';
|
||||
div.style.justifyContent = 'center';
|
||||
div.style.alignItems = 'stretch';
|
||||
|
||||
document.body && document.body.appendChild(div);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
function findDevToolsNode(): HTMLElement | null {
|
||||
return document.querySelector('#' + DEV_TOOLS_NODE_ID);
|
||||
}
|
||||
|
||||
function attachDevTools(target: Element | Text, devToolsNode: HTMLElement) {
|
||||
target.appendChild(devToolsNode);
|
||||
devToolsNode.style.display = 'flex';
|
||||
}
|
||||
|
||||
function detachDevTools(devToolsNode: HTMLElement) {
|
||||
devToolsNode.style.display = 'none';
|
||||
document.body && document.body.appendChild(devToolsNode);
|
||||
}
|
||||
|
||||
const CONNECTED = 'DevTools connected';
|
||||
|
||||
type GrabMetroDeviceStoreProps = {metroDevice: MetroDevice};
|
||||
type GrabMetroDeviceOwnProps = {onHasDevice(device: MetroDevice): void};
|
||||
|
||||
// Utility component to grab the metroDevice from the store if there is one
|
||||
const GrabMetroDevice = connect<
|
||||
GrabMetroDeviceStoreProps,
|
||||
{},
|
||||
GrabMetroDeviceOwnProps,
|
||||
ReduxState
|
||||
>(({connections: {devices}}) => ({
|
||||
metroDevice: devices.find(
|
||||
device => device.os === 'Metro' && !device.isArchived,
|
||||
) as MetroDevice,
|
||||
}))(function({
|
||||
metroDevice,
|
||||
onHasDevice,
|
||||
}: GrabMetroDeviceStoreProps & GrabMetroDeviceOwnProps) {
|
||||
useEffect(() => {
|
||||
onHasDevice(metroDevice);
|
||||
}, [metroDevice]);
|
||||
return null;
|
||||
});
|
||||
|
||||
const SUPPORTED_OCULUS_DEVICE_TYPES = ['quest', 'go', 'pacific'];
|
||||
|
||||
enum ConnectionStatus {
|
||||
Initializing = 'Initializing...',
|
||||
WaitingForReload = 'Waiting for connection from device...',
|
||||
Connected = 'Connected',
|
||||
Error = 'Error',
|
||||
}
|
||||
|
||||
export default class ReactDevTools extends FlipperDevicePlugin<
|
||||
{
|
||||
status: string;
|
||||
},
|
||||
any,
|
||||
{}
|
||||
> {
|
||||
static supportsDevice(device: Device) {
|
||||
return !device.isArchived && device.os === 'Metro';
|
||||
}
|
||||
|
||||
pollHandle?: NodeJS.Timeout;
|
||||
containerRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
connectionStatus: ConnectionStatus = ConnectionStatus.Initializing;
|
||||
metroDevice?: MetroDevice;
|
||||
isMounted = true;
|
||||
|
||||
state = {
|
||||
status: 'initializing',
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.bootDevTools();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isMounted = false;
|
||||
if (this.pollHandle) {
|
||||
clearTimeout(this.pollHandle);
|
||||
}
|
||||
const devToolsNode = findDevToolsNode();
|
||||
devToolsNode && detachDevTools(devToolsNode);
|
||||
}
|
||||
|
||||
setStatus(connectionStatus: ConnectionStatus, status: string) {
|
||||
this.connectionStatus = connectionStatus;
|
||||
if (!this.isMounted) {
|
||||
return;
|
||||
}
|
||||
if (status.startsWith('The server is listening on')) {
|
||||
this.setState({status: status + ' Waiting for connection...'});
|
||||
} else {
|
||||
this.setState({status});
|
||||
}
|
||||
}
|
||||
|
||||
devtoolsHaveStarted() {
|
||||
return !!findDevToolsNode()?.innerHTML;
|
||||
}
|
||||
|
||||
bootDevTools() {
|
||||
let devToolsNode = findDevToolsNode();
|
||||
if (!devToolsNode) {
|
||||
devToolsNode = createDevToolsNode();
|
||||
}
|
||||
this.initializeDevTools(devToolsNode);
|
||||
this.setStatus(
|
||||
ConnectionStatus.Initializing,
|
||||
'DevTools have been initialized, waiting for connection...',
|
||||
);
|
||||
if (this.devtoolsHaveStarted()) {
|
||||
this.setStatus(ConnectionStatus.Connected, CONNECTED);
|
||||
} else {
|
||||
this.startPollForConnection();
|
||||
}
|
||||
|
||||
attachDevTools(this.containerRef?.current!, devToolsNode);
|
||||
this.startPollForConnection();
|
||||
}
|
||||
|
||||
startPollForConnection(delay = 3000) {
|
||||
this.pollHandle = setTimeout(() => {
|
||||
switch (true) {
|
||||
// Closed already, ignore
|
||||
case !this.isMounted:
|
||||
return;
|
||||
// Found DevTools!
|
||||
case this.devtoolsHaveStarted():
|
||||
this.setStatus(ConnectionStatus.Connected, CONNECTED);
|
||||
return;
|
||||
// Waiting for connection, but we do have an active Metro connection, lets force a reload to enter Dev Mode on app
|
||||
// prettier-ignore
|
||||
case this.connectionStatus === ConnectionStatus.Initializing && !!this.metroDevice?.ws:
|
||||
this.setStatus(
|
||||
ConnectionStatus.WaitingForReload,
|
||||
"Sending 'reload' to Metro to force the DevTools to connect...",
|
||||
);
|
||||
this.metroDevice!.sendCommand('reload');
|
||||
this.startPollForConnection(10000);
|
||||
return;
|
||||
// Waiting for initial connection, but no WS bridge available
|
||||
case this.connectionStatus === ConnectionStatus.Initializing:
|
||||
this.setStatus(
|
||||
ConnectionStatus.WaitingForReload,
|
||||
"The DevTools didn't connect yet. Please trigger the DevMenu in the React Native app, or Reload it to connect",
|
||||
);
|
||||
this.startPollForConnection(10000);
|
||||
return;
|
||||
// Still nothing? Users might not have done manual action, or some other tools have picked it up?
|
||||
case this.connectionStatus === ConnectionStatus.WaitingForReload:
|
||||
this.setStatus(
|
||||
ConnectionStatus.WaitingForReload,
|
||||
"The DevTools didn't connect yet. Please verify your React Native app is in development mode, and that no other instance of the React DevTools are attached to the app already.",
|
||||
);
|
||||
this.startPollForConnection();
|
||||
return;
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
async initializeDevTools(devToolsNode: HTMLElement) {
|
||||
try {
|
||||
this.setStatus(ConnectionStatus.Initializing, 'Waiting for port 8097');
|
||||
const port = await getPort({port: 8097}); // default port for dev tools
|
||||
this.setStatus(
|
||||
ConnectionStatus.Initializing,
|
||||
'Starting DevTools server on ' + port,
|
||||
);
|
||||
ReactDevToolsStandalone.setContentDOMNode(devToolsNode)
|
||||
.setStatusListener(status => {
|
||||
this.setStatus(ConnectionStatus.Initializing, status);
|
||||
})
|
||||
.startServer(port);
|
||||
this.setStatus(ConnectionStatus.Initializing, 'Waiting for device');
|
||||
const device = this.device;
|
||||
|
||||
if (device) {
|
||||
if (
|
||||
device.deviceType === 'physical' ||
|
||||
SUPPORTED_OCULUS_DEVICE_TYPES.includes(device.title.toLowerCase())
|
||||
) {
|
||||
this.setStatus(
|
||||
ConnectionStatus.Initializing,
|
||||
`Setting up reverse port mapping: ${port}:${port}`,
|
||||
);
|
||||
(device as AndroidDevice).reverse([port, port]);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setStatus(
|
||||
ConnectionStatus.Error,
|
||||
'Failed to initialize DevTools: ' + e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View grow>
|
||||
{!this.devtoolsHaveStarted() ? this.renderStatus() : null}
|
||||
<Container ref={this.containerRef} />
|
||||
<GrabMetroDevice
|
||||
onHasDevice={device => {
|
||||
this.metroDevice = device;
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderStatus() {
|
||||
return (
|
||||
<CenteredView>
|
||||
<RoundedSection title={this.connectionStatus}>
|
||||
<Text>{this.state.status}</Text>
|
||||
{(this.connectionStatus === ConnectionStatus.WaitingForReload &&
|
||||
this.metroDevice?.ws) ||
|
||||
this.connectionStatus === ConnectionStatus.Error ? (
|
||||
<Button
|
||||
style={{width: 200, margin: '10px auto 0 auto'}}
|
||||
onClick={() => {
|
||||
this.metroDevice?.sendCommand('reload');
|
||||
this.bootDevTools();
|
||||
}}>
|
||||
Retry
|
||||
</Button>
|
||||
) : null}
|
||||
</RoundedSection>
|
||||
</CenteredView>
|
||||
);
|
||||
}
|
||||
}
|
||||
18
desktop/plugins/reactdevtools/package.json
Normal file
18
desktop/plugins/reactdevtools/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "React",
|
||||
"version": "1.0.1",
|
||||
"main": "index.tsx",
|
||||
"license": "MIT",
|
||||
"keywords": ["flipper-plugin"],
|
||||
"dependencies": {
|
||||
"address": "^1.1.2",
|
||||
"get-port": "^5.0.0",
|
||||
"react-devtools-core": "^4.0.6"
|
||||
},
|
||||
"title": "React DevTools",
|
||||
"icon": "app-react",
|
||||
"bugs": {
|
||||
"email": "danielbuechele@fb.com"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
18
desktop/plugins/reactdevtools/react-devtools-core.d.tsx
Normal file
18
desktop/plugins/reactdevtools/react-devtools-core.d.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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 'react-devtools-core/standalone' {
|
||||
interface DevTools {
|
||||
setContentDOMNode(node: HTMLElement): this;
|
||||
startServer(port: number): this;
|
||||
setStatusListener(listener: (message: string) => void): this;
|
||||
}
|
||||
const DevTools: DevTools;
|
||||
export default DevTools;
|
||||
}
|
||||
110
desktop/plugins/reactdevtools/yarn.lock
Normal file
110
desktop/plugins/reactdevtools/yarn.lock
Normal file
@@ -0,0 +1,110 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
address@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
|
||||
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
|
||||
|
||||
array-filter@~0.0.0:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
|
||||
|
||||
array-map@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
|
||||
|
||||
array-reduce@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
|
||||
|
||||
async-limiter@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
||||
integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
|
||||
|
||||
d@1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
||||
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
|
||||
dependencies:
|
||||
es5-ext "^0.10.50"
|
||||
type "^1.0.1"
|
||||
|
||||
es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14:
|
||||
version "0.10.50"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778"
|
||||
integrity sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==
|
||||
dependencies:
|
||||
es6-iterator "~2.0.3"
|
||||
es6-symbol "~3.1.1"
|
||||
next-tick "^1.0.0"
|
||||
|
||||
es6-iterator@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-symbol@^3, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
|
||||
integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
get-port@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.0.0.tgz#aa22b6b86fd926dd7884de3e23332c9f70c031a6"
|
||||
integrity sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==
|
||||
dependencies:
|
||||
type-fest "^0.3.0"
|
||||
|
||||
jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
|
||||
next-tick@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||
|
||||
react-devtools-core@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.0.6.tgz#681c7349db618856d6df7d31a6a49edee9d9e428"
|
||||
integrity sha512-IhAndVGmV74Bio1BRrlbsonH6bX3XFHgz2uixJFlNjg/Rm264mBveIMwM6+rV3yObSKVnggXRMtJuyWoPk2Smw==
|
||||
dependencies:
|
||||
es6-symbol "^3"
|
||||
shell-quote "^1.6.1"
|
||||
ws "^7"
|
||||
|
||||
shell-quote@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
|
||||
dependencies:
|
||||
array-filter "~0.0.0"
|
||||
array-map "~0.0.0"
|
||||
array-reduce "~0.0.0"
|
||||
jsonify "~0.0.0"
|
||||
|
||||
type-fest@^0.3.0:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
|
||||
integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==
|
||||
|
||||
type@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179"
|
||||
integrity sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==
|
||||
|
||||
ws@^7:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.1.tgz#f9942dc868b6dffb72c14fd8f2ba05f77a4d5983"
|
||||
integrity sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A==
|
||||
dependencies:
|
||||
async-limiter "^1.0.0"
|
||||
Reference in New Issue
Block a user