Initialise flipper-ui-browser with socket connection
Summary: This diff sets up the socket connection between flipper-browser and flipper-server, and verifies that the initial UI initialisation work (e.g. `get-config` command works). The initial RenderHost is initialised as well based on the config and browser APIs. Note that flipper-ui-core itself isn't started yet, as that has still a plethora of node imports, so Metro will correctly refuse to bundle Not in this diff * remove Node usage from flipper-ui-core * implement all RenderHost APIs Reviewed By: aigoncharov Differential Revision: D32644074 fbshipit-source-id: 2c8065caf0191771a3867b69a431ca50eeb7a5a3
This commit is contained in:
committed by
Facebook GitHub Bot
parent
29a907c733
commit
5d45bd741b
@@ -270,7 +270,7 @@ function showCompileError() {
|
||||
// Symbolicating compile errors is wasted effort
|
||||
// because the stack trace is meaningless:
|
||||
(error as any).preventSymbolication = true;
|
||||
(window as any).flipperShowError?.(message);
|
||||
window.flipperShowError?.(message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
120
desktop/flipper-ui-browser/src/flipperServerConnection.tsx
Normal file
120
desktop/flipper-ui-browser/src/flipperServerConnection.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 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 EventEmitter from 'eventemitter3';
|
||||
import {FlipperServer} from 'flipper-common';
|
||||
import {io, Socket} from 'socket.io-client';
|
||||
|
||||
const CONNECTION_TIMEOUT = 30 * 1000;
|
||||
const EXEC_TIMOUT = 10 * 1000;
|
||||
|
||||
export function createFlipperServer(): Promise<FlipperServer> {
|
||||
// TODO: polish this all!
|
||||
window.flipperShowError?.('Connecting to server...');
|
||||
return new Promise<FlipperServer>((resolve, reject) => {
|
||||
const initialConnectionTimeout = setTimeout(() => {
|
||||
reject(
|
||||
new Error('Failed to connect to Flipper server in a timely manner'),
|
||||
);
|
||||
}, CONNECTION_TIMEOUT);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
// TODO: recycle the socket that is created in index.web.dev.html?
|
||||
const socket: Socket = io();
|
||||
const pendingRequests: Map<
|
||||
number,
|
||||
{
|
||||
resolve: (data: any) => void;
|
||||
reject: (data: any) => void;
|
||||
timeout: ReturnType<typeof setTimeout>;
|
||||
}
|
||||
> = new Map();
|
||||
let requestId = 0;
|
||||
let connected = false;
|
||||
|
||||
socket.on('connect', () => {
|
||||
window?.flipperHideError?.();
|
||||
console.log('Socket to Flipper server connected');
|
||||
connected = true;
|
||||
});
|
||||
|
||||
socket.once('connect', () => {
|
||||
// only relevant for the first connect
|
||||
resolve(flipperServer);
|
||||
clearTimeout(initialConnectionTimeout);
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
window?.flipperShowError?.('WebSocket connection lost');
|
||||
console.warn('Socket to Flipper server disconnected');
|
||||
connected = false;
|
||||
});
|
||||
|
||||
socket.on('exec-response', (id: number, data: any) => {
|
||||
console.debug('exec <<<', id, data);
|
||||
const entry = pendingRequests.get(id);
|
||||
if (!entry) {
|
||||
console.warn(`Unknown request id `, id);
|
||||
} else {
|
||||
pendingRequests.delete(id);
|
||||
clearTimeout(entry.timeout);
|
||||
entry.resolve(data);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('exec-response-error', (id: number, error: any) => {
|
||||
console.debug('exec <<< [SERVER ERROR]', id, error);
|
||||
const entry = pendingRequests.get(id);
|
||||
if (!entry) {
|
||||
console.warn(`Unknown request id `, id);
|
||||
} else {
|
||||
pendingRequests.delete(id);
|
||||
clearTimeout(entry.timeout);
|
||||
entry.reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('event', (eventType, data) => {
|
||||
eventEmitter.emit(eventType, data);
|
||||
});
|
||||
|
||||
const flipperServer: FlipperServer = {
|
||||
async connect() {},
|
||||
close() {},
|
||||
exec(command, ...args): any {
|
||||
if (connected) {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
const id = ++requestId;
|
||||
console.debug('exec >>>', id, command, args);
|
||||
|
||||
pendingRequests.set(id, {
|
||||
resolve,
|
||||
reject,
|
||||
timeout: setInterval(() => {
|
||||
pendingRequests.delete(id);
|
||||
reject(new Error(`Timeout for command '${command}'`));
|
||||
}, EXEC_TIMOUT),
|
||||
});
|
||||
|
||||
socket.emit('exec', id, command, args);
|
||||
});
|
||||
// socket.
|
||||
} else {
|
||||
throw new Error('Not connected to Flipper Server');
|
||||
}
|
||||
},
|
||||
on(event, callback) {
|
||||
eventEmitter.on(event, callback);
|
||||
},
|
||||
off(event, callback) {
|
||||
eventEmitter.off(event, callback);
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
25
desktop/flipper-ui-browser/src/global.ts
Normal file
25
desktop/flipper-ui-browser/src/global.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 {RenderHost} from 'flipper-ui-core';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
flipperConfig: {
|
||||
theme: 'light' | 'dark' | 'system';
|
||||
entryPoint: string;
|
||||
debug: boolean;
|
||||
};
|
||||
|
||||
FlipperRenderHostInstance: RenderHost;
|
||||
|
||||
flipperShowError?(error: string): void;
|
||||
flipperHideError?(): void;
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,86 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {getLogger, Logger, setLoggerInstance} from 'flipper-common';
|
||||
import {initializeRenderHost} from './initializeRenderHost';
|
||||
import {createFlipperServer} from './flipperServerConnection';
|
||||
|
||||
document.getElementById('root')!.innerText = 'flipper-ui-browser started';
|
||||
|
||||
async function start() {
|
||||
const logger = createDelegatedLogger();
|
||||
setLoggerInstance(logger);
|
||||
|
||||
const flipperServer = await createFlipperServer();
|
||||
|
||||
await flipperServer.connect();
|
||||
const flipperServerConfig = await flipperServer.exec('get-config');
|
||||
|
||||
initializeRenderHost(flipperServer, flipperServerConfig);
|
||||
|
||||
// By turning this in a require, we force the JS that the body of this module (init) has completed (initializeElectron),
|
||||
// before starting the rest of the Flipper process.
|
||||
// This prevent issues where the render host is referred at module initialisation level,
|
||||
// but not set yet, which might happen when using normal imports.
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
// TODO: replace
|
||||
window.flipperShowError?.('Connected to Flipper Server successfully');
|
||||
// TODO: require('flipper-ui-core').startFlipperDesktop(flipperServer);
|
||||
}
|
||||
|
||||
start().catch((e) => {
|
||||
console.error('Failed to start flipper-ui-browser', e);
|
||||
window.flipperShowError?.('Failed to start flipper-ui-browser: ' + e);
|
||||
});
|
||||
|
||||
// getLogger() is not yet created when the electron app starts.
|
||||
// we can't create it here yet, as the real logger is wired up to
|
||||
// the redux store and the rest of the world. So we create a delegating logger
|
||||
// that uses a simple implementation until the real one comes available
|
||||
function createDelegatedLogger(): Logger {
|
||||
const naiveLogger: Logger = {
|
||||
track(...args: [any, any, any?, any?]) {
|
||||
console.warn('(skipper track)', args);
|
||||
},
|
||||
trackTimeSince(...args: [any, any, any?]) {
|
||||
console.warn('(skipped trackTimeSince)', args);
|
||||
},
|
||||
debug(...args: any[]) {
|
||||
console.debug(...args);
|
||||
},
|
||||
error(...args: any[]) {
|
||||
console.error(...args);
|
||||
console.warn('(skipped error reporting)');
|
||||
},
|
||||
warn(...args: any[]) {
|
||||
console.warn(...args);
|
||||
console.warn('(skipped error reporting)');
|
||||
},
|
||||
info(...args: any[]) {
|
||||
console.info(...args);
|
||||
},
|
||||
};
|
||||
// will be overwrittingen later
|
||||
setLoggerInstance(naiveLogger);
|
||||
|
||||
return {
|
||||
track() {
|
||||
// noop
|
||||
},
|
||||
trackTimeSince() {
|
||||
// noop
|
||||
},
|
||||
debug(...args: any[]) {
|
||||
getLogger().debug(...args);
|
||||
},
|
||||
error(...args: any[]) {
|
||||
getLogger().error(...args);
|
||||
},
|
||||
warn(...args: any[]) {
|
||||
getLogger().warn(...args);
|
||||
},
|
||||
info(...args: any[]) {
|
||||
getLogger().info(...args);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
74
desktop/flipper-ui-browser/src/initializeRenderHost.tsx
Normal file
74
desktop/flipper-ui-browser/src/initializeRenderHost.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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 {FlipperServer, FlipperServerConfig} from 'flipper-common';
|
||||
|
||||
export function initializeRenderHost(
|
||||
flipperServer: FlipperServer,
|
||||
flipperServerConfig: FlipperServerConfig,
|
||||
) {
|
||||
window.FlipperRenderHostInstance = {
|
||||
processId: 0,
|
||||
isProduction: window.flipperConfig.debug !== true,
|
||||
readTextFromClipboard() {
|
||||
// TODO:
|
||||
return undefined;
|
||||
},
|
||||
writeTextToClipboard(_text: string) {
|
||||
// TODO:
|
||||
},
|
||||
async importFile() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
async exportFile() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
openLink(url: string) {
|
||||
window.open(url, '_blank');
|
||||
},
|
||||
registerShortcut(_shortcut, _callback) {
|
||||
// TODO:
|
||||
return () => {};
|
||||
},
|
||||
hasFocus() {
|
||||
return document.hasFocus();
|
||||
},
|
||||
onIpcEvent(event) {
|
||||
console.warn('onIpcEvent not available', event);
|
||||
},
|
||||
sendIpcEvent(event, ..._args: any[]) {
|
||||
console.warn('sendIpcEvent not available', event);
|
||||
},
|
||||
shouldUseDarkColors() {
|
||||
return !!(
|
||||
window.flipperConfig.theme === 'dark' ||
|
||||
(window.flipperConfig.theme === 'system' &&
|
||||
window.matchMedia?.('(prefers-color-scheme: dark)'))
|
||||
);
|
||||
},
|
||||
restartFlipper() {
|
||||
// TODO: restart server as well
|
||||
window.location.reload();
|
||||
},
|
||||
loadDefaultPlugins: getDefaultPluginsIndex,
|
||||
serverConfig: flipperServerConfig,
|
||||
GK(gatekeeper) {
|
||||
return flipperServerConfig.gatekeepers[gatekeeper] ?? false;
|
||||
},
|
||||
flipperServer,
|
||||
};
|
||||
}
|
||||
|
||||
function getDefaultPluginsIndex() {
|
||||
// TODO:
|
||||
return {};
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
// const index = require('../defaultPlugins');
|
||||
// return index.default || index;
|
||||
}
|
||||
Reference in New Issue
Block a user