Files
flipper/desktop/app/src/dispatcher/metroDevice.tsx
Michel Weststrate 6a30899803 Kill onRegisterDevice
Summary: `onRegisterDevice` device abstraction was only used by the CrashReporterPlugin, and since with Sandy plugin lifecycles every plugin can do 'on-load' logic, we don't need it anymore.

Reviewed By: priteshrnandgaonkar

Differential Revision: D27046711

fbshipit-source-id: 16c567c60ed29a50017d525a2b707ee696a99e62
2021-03-16 15:03:48 -07:00

132 lines
3.6 KiB
TypeScript

/**
* 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 {Store} from '../reducers/index';
import {Logger} from '../fb-interfaces/Logger';
import MetroDevice from '../devices/MetroDevice';
import http from 'http';
import {addErrorNotification} from '../reducers/notifications';
import {destroyDevice} from '../reducers/connections';
const METRO_PORT = 8081;
const METRO_HOST = 'localhost';
const METRO_URL = `http://${METRO_HOST}:${METRO_PORT}`;
const METRO_LOGS_ENDPOINT = `ws://${METRO_HOST}:${METRO_PORT}/events`;
const METRO_MESSAGE = ['React Native packager is running', 'Metro is running'];
const QUERY_INTERVAL = 5000;
async function isMetroRunning(): Promise<boolean> {
return new Promise((resolve) => {
// We use Node's http library, rather than fetch api, as the latter cannot supress network errors being shown in the devtools console
// which generates a lot of noise
http
.get(METRO_URL, (resp) => {
let data = '';
resp
.on('data', (chunk) => {
data += chunk;
})
.on('end', () => {
const isMetro = METRO_MESSAGE.some((msg) => data.includes(msg));
resolve(isMetro);
});
})
.on('error', (err: any) => {
if (err.code !== 'ECONNREFUSED' && err.code !== 'ECONNRESET') {
console.error('Could not connect to METRO ' + err);
}
resolve(false);
});
});
}
export async function registerMetroDevice(
ws: WebSocket | undefined,
store: Store,
logger: Logger,
) {
const metroDevice = new MetroDevice(METRO_URL, ws);
logger.track('usage', 'register-device', {
os: 'Metro',
name: metroDevice.title,
});
metroDevice.loadDevicePlugins(
store.getState().plugins.devicePlugins,
store.getState().connections.enabledDevicePlugins,
);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: metroDevice,
serial: METRO_URL,
});
}
export default (store: Store, logger: Logger) => {
let timeoutHandle: NodeJS.Timeout;
let ws: WebSocket | undefined;
let unregistered = false;
async function tryConnectToMetro() {
if (ws) {
return;
}
if (await isMetroRunning()) {
const _ws = new WebSocket(METRO_LOGS_ENDPOINT);
_ws.onopen = () => {
clearTimeout(guard);
ws = _ws;
registerMetroDevice(ws, store, logger);
};
_ws.onclose = _ws.onerror = () => {
if (!unregistered) {
unregistered = true;
clearTimeout(guard);
ws = undefined;
destroyDevice(store, logger, METRO_URL);
scheduleNext();
}
};
const guard = setTimeout(() => {
// Metro is running, but didn't respond to /events endpoint
store.dispatch(
addErrorNotification(
'Failed to connect to Metro',
`Flipper did find a running Metro instance, but couldn't connect to the logs. Probably your React Native version is too old to support Flipper. Cause: Failed to get a connection to ${METRO_LOGS_ENDPOINT} in a timely fashion`,
),
);
registerMetroDevice(undefined, store, logger);
// Note: no scheduleNext, we won't retry until restart
}, 5000);
} else {
scheduleNext();
}
}
function scheduleNext() {
timeoutHandle = setTimeout(tryConnectToMetro, QUERY_INTERVAL);
}
tryConnectToMetro();
// cleanup method
return () => {
if (ws) {
ws.close();
}
if (timeoutHandle) {
clearInterval(timeoutHandle);
}
};
};