Remove remaining Electron imports from product code: paths & env
Summary: This diff removes most remaining Electron imports, by storing env and path constants on the RenderHost. As nice side effect those paths are all cached now as well. To make sure RenderHost is initialised before Flipper itself, forced loading Flipper through a require. Otherwise the setup is super sensitive to circular import statements, since a lot of module initialisation code depends on those paths / env vars. Reviewed By: nikoant Differential Revision: D31992230 fbshipit-source-id: 91beb430902272aaf4b051b35cdf12d2fc993347
This commit is contained in:
committed by
Facebook GitHub Bot
parent
dba09542f9
commit
2e7015388c
@@ -7,10 +7,20 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NotificationEvents} from './dispatcher/notifications';
|
import type {NotificationEvents} from './dispatcher/notifications';
|
||||||
import {PluginNotification} from './reducers/notifications';
|
import type {PluginNotification} from './reducers/notifications';
|
||||||
import type {NotificationConstructorOptions} from 'electron';
|
import type {NotificationConstructorOptions} from 'electron';
|
||||||
import {FlipperLib} from 'flipper-plugin';
|
import type {FlipperLib} from 'flipper-plugin';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
type ENVIRONMENT_VARIABLES = 'NODE_ENV' | 'DEV_SERVER_URL' | 'CONFIG';
|
||||||
|
type ENVIRONMENT_PATHS =
|
||||||
|
| 'appPath'
|
||||||
|
| 'homePath'
|
||||||
|
| 'execPath'
|
||||||
|
| 'staticPath'
|
||||||
|
| 'tempPath'
|
||||||
|
| 'desktopPath';
|
||||||
|
|
||||||
// Events that are emitted from the main.ts ovr the IPC process bridge in Electron
|
// Events that are emitted from the main.ts ovr the IPC process bridge in Electron
|
||||||
type MainProcessEvents = {
|
type MainProcessEvents = {
|
||||||
@@ -45,6 +55,7 @@ type ChildProcessEvents = {
|
|||||||
export interface RenderHost {
|
export interface RenderHost {
|
||||||
readonly processId: number;
|
readonly processId: number;
|
||||||
readTextFromClipboard(): string | undefined;
|
readTextFromClipboard(): string | undefined;
|
||||||
|
writeTextToClipboard(text: string): void;
|
||||||
showSaveDialog?: FlipperLib['showSaveDialog'];
|
showSaveDialog?: FlipperLib['showSaveDialog'];
|
||||||
showOpenDialog?: FlipperLib['showOpenDialog'];
|
showOpenDialog?: FlipperLib['showOpenDialog'];
|
||||||
showSelectDirectoryDialog?(defaultPath?: string): Promise<string | undefined>;
|
showSelectDirectoryDialog?(defaultPath?: string): Promise<string | undefined>;
|
||||||
@@ -60,6 +71,9 @@ export interface RenderHost {
|
|||||||
): void;
|
): void;
|
||||||
shouldUseDarkColors(): boolean;
|
shouldUseDarkColors(): boolean;
|
||||||
restartFlipper(update?: boolean): void;
|
restartFlipper(update?: boolean): void;
|
||||||
|
env: Partial<Record<ENVIRONMENT_VARIABLES, string>>;
|
||||||
|
paths: Record<ENVIRONMENT_PATHS, string>;
|
||||||
|
openLink(url: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let renderHostInstance: RenderHost | undefined;
|
let renderHostInstance: RenderHost | undefined;
|
||||||
@@ -81,6 +95,7 @@ if (process.env.NODE_ENV === 'test') {
|
|||||||
readTextFromClipboard() {
|
readTextFromClipboard() {
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
writeTextToClipboard() {},
|
||||||
registerShortcut() {},
|
registerShortcut() {},
|
||||||
hasFocus() {
|
hasFocus() {
|
||||||
return true;
|
return true;
|
||||||
@@ -91,5 +106,15 @@ if (process.env.NODE_ENV === 'test') {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
restartFlipper() {},
|
restartFlipper() {},
|
||||||
|
openLink() {},
|
||||||
|
env: process.env,
|
||||||
|
paths: {
|
||||||
|
appPath: process.cwd(),
|
||||||
|
homePath: `/dev/null`,
|
||||||
|
desktopPath: `/dev/null`,
|
||||||
|
execPath: process.cwd(),
|
||||||
|
staticPath: path.join(process.cwd(), 'static'),
|
||||||
|
tempPath: `/tmp/`,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import React, {useState, useEffect, useCallback} from 'react';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import open from 'open';
|
import open from 'open';
|
||||||
import {capture, CAPTURE_LOCATION, getFileName} from '../utils/screenshot';
|
import {capture, getCaptureLocation, getFileName} from '../utils/screenshot';
|
||||||
import {CameraOutlined, VideoCameraOutlined} from '@ant-design/icons';
|
import {CameraOutlined, VideoCameraOutlined} from '@ant-design/icons';
|
||||||
import {useStore} from '../utils/useStore';
|
import {useStore} from '../utils/useStore';
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ export default function ScreenCaptureButtons() {
|
|||||||
}
|
}
|
||||||
if (!isRecording) {
|
if (!isRecording) {
|
||||||
setIsRecording(true);
|
setIsRecording(true);
|
||||||
const videoPath = path.join(CAPTURE_LOCATION, getFileName('mp4'));
|
const videoPath = path.join(getCaptureLocation(), getFileName('mp4'));
|
||||||
return selectedDevice.startScreenCapture(videoPath).catch((e) => {
|
return selectedDevice.startScreenCapture(videoPath).catch((e) => {
|
||||||
console.error('Failed to start recording', e);
|
console.error('Failed to start recording', e);
|
||||||
message.error('Failed to start recording' + e);
|
message.error('Failed to start recording' + e);
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import BaseDevice from '../devices/BaseDevice';
|
|||||||
import {ClientDescription, timeout} from 'flipper-common';
|
import {ClientDescription, timeout} from 'flipper-common';
|
||||||
import {reportPlatformFailures} from 'flipper-common';
|
import {reportPlatformFailures} from 'flipper-common';
|
||||||
import {sideEffect} from '../utils/sideEffect';
|
import {sideEffect} from '../utils/sideEffect';
|
||||||
import {getAppTempPath, getStaticPath} from '../utils/pathUtils';
|
import {getStaticPath} from '../utils/pathUtils';
|
||||||
import constants from '../fb-stubs/constants';
|
import constants from '../fb-stubs/constants';
|
||||||
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
|
||||||
export default async (store: Store, logger: Logger) => {
|
export default async (store: Store, logger: Logger) => {
|
||||||
const {enableAndroid, androidHome, idbPath, enableIOS, enablePhysicalIOS} =
|
const {enableAndroid, androidHome, idbPath, enableIOS, enablePhysicalIOS} =
|
||||||
@@ -33,7 +34,7 @@ export default async (store: Store, logger: Logger) => {
|
|||||||
enableIOS,
|
enableIOS,
|
||||||
enablePhysicalIOS,
|
enablePhysicalIOS,
|
||||||
staticPath: getStaticPath(),
|
staticPath: getStaticPath(),
|
||||||
tmpPath: getAppTempPath(),
|
tmpPath: getRenderHostInstance().paths.tempPath,
|
||||||
validWebSocketOrigins: constants.VALID_WEB_SOCKET_REQUEST_ORIGIN_PREFIXES,
|
validWebSocketOrigins: constants.VALID_WEB_SOCKET_REQUEST_ORIGIN_PREFIXES,
|
||||||
},
|
},
|
||||||
logger,
|
logger,
|
||||||
|
|||||||
@@ -15,17 +15,27 @@ import {
|
|||||||
_LoggerContext,
|
_LoggerContext,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
// eslint-disable-next-line flipper/no-electron-remote-imports
|
||||||
import {ipcRenderer, remote, SaveDialogReturnValue} from 'electron';
|
import {
|
||||||
import {setRenderHostInstance} from '../RenderHost';
|
ipcRenderer,
|
||||||
import {clipboard} from 'electron';
|
remote,
|
||||||
import restart from './restartFlipper';
|
SaveDialogReturnValue,
|
||||||
|
clipboard,
|
||||||
|
shell,
|
||||||
|
} from 'electron';
|
||||||
|
import {getRenderHostInstance, setRenderHostInstance} from '../RenderHost';
|
||||||
|
import isProduction from '../utils/isProduction';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
export function initializeElectron() {
|
export function initializeElectron() {
|
||||||
|
const app = remote.app;
|
||||||
setRenderHostInstance({
|
setRenderHostInstance({
|
||||||
processId: remote.process.pid,
|
processId: remote.process.pid,
|
||||||
readTextFromClipboard() {
|
readTextFromClipboard() {
|
||||||
return clipboard.readText();
|
return clipboard.readText();
|
||||||
},
|
},
|
||||||
|
writeTextToClipboard(text: string) {
|
||||||
|
clipboard.writeText(text);
|
||||||
|
},
|
||||||
async showSaveDialog(options) {
|
async showSaveDialog(options) {
|
||||||
return (await remote.dialog.showSaveDialog(options))?.filePath;
|
return (await remote.dialog.showSaveDialog(options))?.filePath;
|
||||||
},
|
},
|
||||||
@@ -56,6 +66,9 @@ export function initializeElectron() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
openLink(url: string) {
|
||||||
|
shell.openExternal(url);
|
||||||
|
},
|
||||||
registerShortcut(shortcut, callback) {
|
registerShortcut(shortcut, callback) {
|
||||||
remote.globalShortcut.register(shortcut, callback);
|
remote.globalShortcut.register(shortcut, callback);
|
||||||
},
|
},
|
||||||
@@ -76,5 +89,50 @@ export function initializeElectron() {
|
|||||||
restartFlipper() {
|
restartFlipper() {
|
||||||
restart();
|
restart();
|
||||||
},
|
},
|
||||||
|
env: process.env,
|
||||||
|
paths: {
|
||||||
|
appPath: app.getAppPath(),
|
||||||
|
homePath: app.getPath('home'),
|
||||||
|
execPath: process.execPath || remote.process.execPath,
|
||||||
|
staticPath: getStaticDir(),
|
||||||
|
tempPath: app.getPath('temp'),
|
||||||
|
desktopPath: app.getPath('desktop'),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStaticDir() {
|
||||||
|
let _staticPath = path.resolve(__dirname, '..', '..', '..', 'static');
|
||||||
|
if (fs.existsSync(_staticPath)) {
|
||||||
|
return _staticPath;
|
||||||
|
}
|
||||||
|
if (remote && fs.existsSync(remote.app.getAppPath())) {
|
||||||
|
_staticPath = path.join(remote.app.getAppPath());
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(_staticPath)) {
|
||||||
|
throw new Error('Static path does not exist: ' + _staticPath);
|
||||||
|
}
|
||||||
|
return _staticPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restart(update: boolean = false) {
|
||||||
|
if (isProduction()) {
|
||||||
|
if (update) {
|
||||||
|
const options = {
|
||||||
|
args: process.argv
|
||||||
|
.splice(0, 1)
|
||||||
|
.filter((arg) => arg !== '--no-launcher' && arg !== '--no-updater'),
|
||||||
|
};
|
||||||
|
remote.app.relaunch(options);
|
||||||
|
} else {
|
||||||
|
remote.app.relaunch();
|
||||||
|
}
|
||||||
|
remote.app.exit();
|
||||||
|
} else {
|
||||||
|
// Relaunching the process with the standard way doesn't work in dev mode.
|
||||||
|
// So instead we're sending a signal to dev server to kill the current instance of electron and launch new.
|
||||||
|
fetch(`${getRenderHostInstance().env.DEV_SERVER_URL}/_restartElectron`, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
|
||||||
import {remote} from 'electron';
|
|
||||||
import isProduction from '../utils/isProduction';
|
|
||||||
|
|
||||||
export default function restart(update: boolean = false) {
|
|
||||||
if (isProduction()) {
|
|
||||||
if (update) {
|
|
||||||
const options = {
|
|
||||||
args: process.argv
|
|
||||||
.splice(0, 1)
|
|
||||||
.filter((arg) => arg !== '--no-launcher' && arg !== '--no-updater'),
|
|
||||||
};
|
|
||||||
remote.app.relaunch(options);
|
|
||||||
} else {
|
|
||||||
remote.app.relaunch();
|
|
||||||
}
|
|
||||||
remote.app.exit();
|
|
||||||
} else {
|
|
||||||
// Relaunching the process with the standard way doesn't work in dev mode.
|
|
||||||
// So instead we're sending a signal to dev server to kill the current instance of electron and launch new.
|
|
||||||
fetch(`${remote.process.env.DEV_SERVER_URL}/_restartElectron`, {
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,55 +7,13 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Provider} from 'react-redux';
|
import {initializeElectron} from './electron/initializeElectron';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
|
|
||||||
import GK from './fb-stubs/GK';
|
import GK from './fb-stubs/GK';
|
||||||
import {init as initLogger} from './fb-stubs/Logger';
|
|
||||||
import {SandyApp} from './sandy-chrome/SandyApp';
|
|
||||||
import setupPrefetcher from './fb-stubs/Prefetcher';
|
|
||||||
import {Persistor, persistStore} from 'redux-persist';
|
|
||||||
import {Store} from './reducers/index';
|
|
||||||
import dispatcher from './dispatcher/index';
|
|
||||||
import TooltipProvider from './ui/components/TooltipProvider';
|
|
||||||
import config from './utils/processConfig';
|
|
||||||
import {initLauncherHooks} from './utils/launcher';
|
|
||||||
import {setPersistor} from './utils/persistor';
|
|
||||||
import React from 'react';
|
|
||||||
import path from 'path';
|
|
||||||
import {getStore} from './store';
|
|
||||||
import {cache} from '@emotion/css';
|
|
||||||
import {CacheProvider} from '@emotion/react';
|
|
||||||
import {enableMapSet} from 'immer';
|
import {enableMapSet} from 'immer';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import {initializeFlipperLibImplementation} from './utils/flipperLibImplementation';
|
|
||||||
import {enableConsoleHook} from './chrome/ConsoleLogs';
|
initializeElectron();
|
||||||
import {sideEffect} from './utils/sideEffect';
|
|
||||||
import {
|
|
||||||
_NuxManagerContext,
|
|
||||||
_createNuxManager,
|
|
||||||
_setGlobalInteractionReporter,
|
|
||||||
Logger,
|
|
||||||
_LoggerContext,
|
|
||||||
Layout,
|
|
||||||
theme,
|
|
||||||
getFlipperLib,
|
|
||||||
} from 'flipper-plugin';
|
|
||||||
import isProduction from './utils/isProduction';
|
|
||||||
import {Button, Input, Result, Typography} from 'antd';
|
|
||||||
import constants from './fb-stubs/constants';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import {CopyOutlined} from '@ant-design/icons';
|
|
||||||
import {getVersionString} from './utils/versionString';
|
|
||||||
import {PersistGate} from 'redux-persist/integration/react';
|
|
||||||
import {
|
|
||||||
setLoggerInstance,
|
|
||||||
setUserSessionManagerInstance,
|
|
||||||
GK as flipperCommonGK,
|
|
||||||
} from 'flipper-common';
|
|
||||||
import {internGraphPOSTAPIRequest} from './fb-stubs/user';
|
|
||||||
import {getRenderHostInstance} from './RenderHost';
|
|
||||||
import {initializeElectron} from './electron/initializeElectron';
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') {
|
if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') {
|
||||||
// By default Node.JS has its internal certificate storage and doesn't use
|
// By default Node.JS has its internal certificate storage and doesn't use
|
||||||
@@ -70,187 +28,9 @@ enableMapSet();
|
|||||||
|
|
||||||
GK.init();
|
GK.init();
|
||||||
|
|
||||||
class AppFrame extends React.Component<
|
// By turning this in a require, we force the JS that the body of this module (init) has completed (initializeElectron),
|
||||||
{logger: Logger; persistor: Persistor},
|
// before starting the rest of the Flipper process.
|
||||||
{error: any; errorInfo: any}
|
// This prevent issues where the render host is referred at module initialisation level,
|
||||||
> {
|
// but not set yet, which might happen when using normal imports.
|
||||||
state = {error: undefined as any, errorInfo: undefined as any};
|
// eslint-disable-next-line import/no-commonjs
|
||||||
|
require('./startFlipperDesktop');
|
||||||
getError() {
|
|
||||||
return this.state.error
|
|
||||||
? `${
|
|
||||||
this.state.error
|
|
||||||
}\n\nFlipper version: ${getVersionString()}\n\nComponent stack:\n${
|
|
||||||
this.state.errorInfo?.componentStack
|
|
||||||
}\n\nError stacktrace:\n${this.state.error?.stack}`
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {logger, persistor} = this.props;
|
|
||||||
return this.state.error ? (
|
|
||||||
<Layout.Container grow center pad={80} style={{height: '100%'}}>
|
|
||||||
<Layout.Top style={{maxWidth: 800, height: '100%'}}>
|
|
||||||
<Result
|
|
||||||
status="error"
|
|
||||||
title="Detected a Flipper crash"
|
|
||||||
subTitle={
|
|
||||||
<p>
|
|
||||||
A crash was detected in the Flipper chrome. Filing a{' '}
|
|
||||||
<Typography.Link
|
|
||||||
href={
|
|
||||||
constants.IS_PUBLIC_BUILD
|
|
||||||
? 'https://github.com/facebook/flipper/issues/new/choose'
|
|
||||||
: constants.FEEDBACK_GROUP_LINK
|
|
||||||
}>
|
|
||||||
bug report
|
|
||||||
</Typography.Link>{' '}
|
|
||||||
would be appreciated! Please include the details below.
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
extra={[
|
|
||||||
<Button
|
|
||||||
key="copy_error"
|
|
||||||
icon={<CopyOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
getFlipperLib().writeTextToClipboard(this.getError());
|
|
||||||
}}>
|
|
||||||
Copy error
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
key="retry_error"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
this.setState({error: undefined, errorInfo: undefined});
|
|
||||||
}}>
|
|
||||||
Retry
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<CodeBlock value={this.getError()} readOnly />
|
|
||||||
</Layout.Top>
|
|
||||||
</Layout.Container>
|
|
||||||
) : (
|
|
||||||
<_LoggerContext.Provider value={logger}>
|
|
||||||
<Provider store={getStore()}>
|
|
||||||
<PersistGate persistor={persistor}>
|
|
||||||
<CacheProvider value={cache}>
|
|
||||||
<TooltipProvider>
|
|
||||||
<_NuxManagerContext.Provider value={_createNuxManager()}>
|
|
||||||
<SandyApp />
|
|
||||||
</_NuxManagerContext.Provider>
|
|
||||||
</TooltipProvider>
|
|
||||||
</CacheProvider>
|
|
||||||
</PersistGate>
|
|
||||||
</Provider>
|
|
||||||
</_LoggerContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidCatch(error: any, errorInfo: any) {
|
|
||||||
console.error(
|
|
||||||
`Flipper chrome crash: ${error}`,
|
|
||||||
error,
|
|
||||||
'\nComponents: ' + errorInfo?.componentStack,
|
|
||||||
);
|
|
||||||
this.setState({
|
|
||||||
error,
|
|
||||||
errorInfo,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProcessState(store: Store) {
|
|
||||||
const settings = store.getState().settingsState;
|
|
||||||
const androidHome = settings.androidHome;
|
|
||||||
const idbPath = settings.idbPath;
|
|
||||||
|
|
||||||
if (!process.env.ANDROID_HOME && !process.env.ANDROID_SDK_ROOT) {
|
|
||||||
process.env.ANDROID_HOME = androidHome;
|
|
||||||
}
|
|
||||||
|
|
||||||
// emulator/emulator is more reliable than tools/emulator, so prefer it if
|
|
||||||
// it exists
|
|
||||||
process.env.PATH =
|
|
||||||
['emulator', 'tools', 'platform-tools']
|
|
||||||
.map((directory) => path.resolve(androidHome, directory))
|
|
||||||
.join(':') +
|
|
||||||
`:${idbPath}` +
|
|
||||||
`:${process.env.PATH}`;
|
|
||||||
|
|
||||||
window.requestIdleCallback(() => {
|
|
||||||
setupPrefetcher(settings);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
initializeElectron();
|
|
||||||
// TODO: centralise all those initialisations in a single configuration call
|
|
||||||
flipperCommonGK.get = (name) => GK.get(name);
|
|
||||||
const store = getStore();
|
|
||||||
const logger = initLogger(store);
|
|
||||||
setLoggerInstance(logger);
|
|
||||||
|
|
||||||
// rehydrate app state before exposing init
|
|
||||||
const persistor = persistStore(store, undefined, () => {
|
|
||||||
// Make sure process state is set before dispatchers run
|
|
||||||
setProcessState(store);
|
|
||||||
dispatcher(store, logger);
|
|
||||||
});
|
|
||||||
|
|
||||||
setPersistor(persistor);
|
|
||||||
|
|
||||||
initializeFlipperLibImplementation(getRenderHostInstance(), store, logger);
|
|
||||||
_setGlobalInteractionReporter((r) => {
|
|
||||||
logger.track('usage', 'interaction', r);
|
|
||||||
if (!isProduction()) {
|
|
||||||
const msg = `[interaction] ${r.scope}:${r.action} in ${r.duration}ms`;
|
|
||||||
if (r.success) console.debug(msg);
|
|
||||||
else console.warn(msg, r.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setUserSessionManagerInstance({
|
|
||||||
internGraphPOSTAPIRequest,
|
|
||||||
});
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<AppFrame logger={logger} persistor={persistor} />,
|
|
||||||
document.getElementById('root'),
|
|
||||||
);
|
|
||||||
initLauncherHooks(config(), store);
|
|
||||||
enableConsoleHook();
|
|
||||||
window.flipperGlobalStoreDispatch = store.dispatch;
|
|
||||||
|
|
||||||
// listen to settings and load the right theme
|
|
||||||
sideEffect(
|
|
||||||
store,
|
|
||||||
{name: 'loadTheme', fireImmediately: false, throttleMs: 500},
|
|
||||||
(state) => state.settingsState.darkMode,
|
|
||||||
(theme) => {
|
|
||||||
let shouldUseDarkMode = false;
|
|
||||||
if (theme === 'dark') {
|
|
||||||
shouldUseDarkMode = true;
|
|
||||||
} else if (theme === 'light') {
|
|
||||||
shouldUseDarkMode = false;
|
|
||||||
} else if (theme === 'system') {
|
|
||||||
shouldUseDarkMode = getRenderHostInstance().shouldUseDarkColors();
|
|
||||||
}
|
|
||||||
(
|
|
||||||
document.getElementById('flipper-theme-import') as HTMLLinkElement
|
|
||||||
).href = `themes/${shouldUseDarkMode ? 'dark' : 'light'}.css`;
|
|
||||||
getRenderHostInstance().sendIpcEvent('setTheme', theme);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
setImmediate(() => {
|
|
||||||
// make sure all modules are loaded
|
|
||||||
// @ts-ignore
|
|
||||||
window.flipperInit = init;
|
|
||||||
window.dispatchEvent(new Event('flipper-store-ready'));
|
|
||||||
});
|
|
||||||
|
|
||||||
const CodeBlock = styled(Input.TextArea)({
|
|
||||||
...theme.monospace,
|
|
||||||
color: theme.textColorSecondary,
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import {Actions} from './index';
|
import {Actions} from './index';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import electron from 'electron';
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
|
||||||
export enum Tristate {
|
export enum Tristate {
|
||||||
True,
|
True,
|
||||||
@@ -105,6 +105,7 @@ function getDefaultAndroidSdkPath() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWindowsSdkPath() {
|
function getWindowsSdkPath() {
|
||||||
const app = electron.app || electron.remote.app;
|
return `${
|
||||||
return `${app.getPath('home')}\\AppData\\Local\\android\\sdk`;
|
getRenderHostInstance().paths.homePath
|
||||||
|
}\\AppData\\Local\\android\\sdk`;
|
||||||
}
|
}
|
||||||
|
|||||||
239
desktop/app/src/startFlipperDesktop.tsx
Normal file
239
desktop/app/src/startFlipperDesktop.tsx
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
/**
|
||||||
|
* 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 {Provider} from 'react-redux';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import GK from './fb-stubs/GK';
|
||||||
|
import {init as initLogger} from './fb-stubs/Logger';
|
||||||
|
import {SandyApp} from './sandy-chrome/SandyApp';
|
||||||
|
import setupPrefetcher from './fb-stubs/Prefetcher';
|
||||||
|
import {Persistor, persistStore} from 'redux-persist';
|
||||||
|
import {Store} from './reducers/index';
|
||||||
|
import dispatcher from './dispatcher/index';
|
||||||
|
import TooltipProvider from './ui/components/TooltipProvider';
|
||||||
|
import config from './utils/processConfig';
|
||||||
|
import {initLauncherHooks} from './utils/launcher';
|
||||||
|
import {setPersistor} from './utils/persistor';
|
||||||
|
import React from 'react';
|
||||||
|
import path from 'path';
|
||||||
|
import {getStore} from './store';
|
||||||
|
import {cache} from '@emotion/css';
|
||||||
|
import {CacheProvider} from '@emotion/react';
|
||||||
|
import {initializeFlipperLibImplementation} from './utils/flipperLibImplementation';
|
||||||
|
import {enableConsoleHook} from './chrome/ConsoleLogs';
|
||||||
|
import {sideEffect} from './utils/sideEffect';
|
||||||
|
import {
|
||||||
|
_NuxManagerContext,
|
||||||
|
_createNuxManager,
|
||||||
|
_setGlobalInteractionReporter,
|
||||||
|
Logger,
|
||||||
|
_LoggerContext,
|
||||||
|
Layout,
|
||||||
|
theme,
|
||||||
|
getFlipperLib,
|
||||||
|
} from 'flipper-plugin';
|
||||||
|
import isProduction from './utils/isProduction';
|
||||||
|
import {Button, Input, Result, Typography} from 'antd';
|
||||||
|
import constants from './fb-stubs/constants';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import {CopyOutlined} from '@ant-design/icons';
|
||||||
|
import {getVersionString} from './utils/versionString';
|
||||||
|
import {PersistGate} from 'redux-persist/integration/react';
|
||||||
|
import {
|
||||||
|
setLoggerInstance,
|
||||||
|
setUserSessionManagerInstance,
|
||||||
|
GK as flipperCommonGK,
|
||||||
|
} from 'flipper-common';
|
||||||
|
import {internGraphPOSTAPIRequest} from './fb-stubs/user';
|
||||||
|
import {getRenderHostInstance} from './RenderHost';
|
||||||
|
|
||||||
|
class AppFrame extends React.Component<
|
||||||
|
{logger: Logger; persistor: Persistor},
|
||||||
|
{error: any; errorInfo: any}
|
||||||
|
> {
|
||||||
|
state = {error: undefined as any, errorInfo: undefined as any};
|
||||||
|
|
||||||
|
getError() {
|
||||||
|
return this.state.error
|
||||||
|
? `${
|
||||||
|
this.state.error
|
||||||
|
}\n\nFlipper version: ${getVersionString()}\n\nComponent stack:\n${
|
||||||
|
this.state.errorInfo?.componentStack
|
||||||
|
}\n\nError stacktrace:\n${this.state.error?.stack}`
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {logger, persistor} = this.props;
|
||||||
|
return this.state.error ? (
|
||||||
|
<Layout.Container grow center pad={80} style={{height: '100%'}}>
|
||||||
|
<Layout.Top style={{maxWidth: 800, height: '100%'}}>
|
||||||
|
<Result
|
||||||
|
status="error"
|
||||||
|
title="Detected a Flipper crash"
|
||||||
|
subTitle={
|
||||||
|
<p>
|
||||||
|
A crash was detected in the Flipper chrome. Filing a{' '}
|
||||||
|
<Typography.Link
|
||||||
|
href={
|
||||||
|
constants.IS_PUBLIC_BUILD
|
||||||
|
? 'https://github.com/facebook/flipper/issues/new/choose'
|
||||||
|
: constants.FEEDBACK_GROUP_LINK
|
||||||
|
}>
|
||||||
|
bug report
|
||||||
|
</Typography.Link>{' '}
|
||||||
|
would be appreciated! Please include the details below.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
extra={[
|
||||||
|
<Button
|
||||||
|
key="copy_error"
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
getFlipperLib().writeTextToClipboard(this.getError());
|
||||||
|
}}>
|
||||||
|
Copy error
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="retry_error"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({error: undefined, errorInfo: undefined});
|
||||||
|
}}>
|
||||||
|
Retry
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<CodeBlock value={this.getError()} readOnly />
|
||||||
|
</Layout.Top>
|
||||||
|
</Layout.Container>
|
||||||
|
) : (
|
||||||
|
<_LoggerContext.Provider value={logger}>
|
||||||
|
<Provider store={getStore()}>
|
||||||
|
<PersistGate persistor={persistor}>
|
||||||
|
<CacheProvider value={cache}>
|
||||||
|
<TooltipProvider>
|
||||||
|
<_NuxManagerContext.Provider value={_createNuxManager()}>
|
||||||
|
<SandyApp />
|
||||||
|
</_NuxManagerContext.Provider>
|
||||||
|
</TooltipProvider>
|
||||||
|
</CacheProvider>
|
||||||
|
</PersistGate>
|
||||||
|
</Provider>
|
||||||
|
</_LoggerContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error: any, errorInfo: any) {
|
||||||
|
console.error(
|
||||||
|
`Flipper chrome crash: ${error}`,
|
||||||
|
error,
|
||||||
|
'\nComponents: ' + errorInfo?.componentStack,
|
||||||
|
);
|
||||||
|
this.setState({
|
||||||
|
error,
|
||||||
|
errorInfo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProcessState(store: Store) {
|
||||||
|
const settings = store.getState().settingsState;
|
||||||
|
const androidHome = settings.androidHome;
|
||||||
|
const idbPath = settings.idbPath;
|
||||||
|
|
||||||
|
if (!process.env.ANDROID_HOME && !process.env.ANDROID_SDK_ROOT) {
|
||||||
|
process.env.ANDROID_HOME = androidHome;
|
||||||
|
}
|
||||||
|
|
||||||
|
// emulator/emulator is more reliable than tools/emulator, so prefer it if
|
||||||
|
// it exists
|
||||||
|
process.env.PATH =
|
||||||
|
['emulator', 'tools', 'platform-tools']
|
||||||
|
.map((directory) => path.resolve(androidHome, directory))
|
||||||
|
.join(':') +
|
||||||
|
`:${idbPath}` +
|
||||||
|
`:${process.env.PATH}`;
|
||||||
|
|
||||||
|
window.requestIdleCallback(() => {
|
||||||
|
setupPrefetcher(settings);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
// TODO: centralise all those initialisations in a single configuration call
|
||||||
|
flipperCommonGK.get = (name) => GK.get(name);
|
||||||
|
const store = getStore();
|
||||||
|
const logger = initLogger(store);
|
||||||
|
setLoggerInstance(logger);
|
||||||
|
|
||||||
|
// rehydrate app state before exposing init
|
||||||
|
const persistor = persistStore(store, undefined, () => {
|
||||||
|
// Make sure process state is set before dispatchers run
|
||||||
|
setProcessState(store);
|
||||||
|
dispatcher(store, logger);
|
||||||
|
});
|
||||||
|
|
||||||
|
setPersistor(persistor);
|
||||||
|
|
||||||
|
initializeFlipperLibImplementation(getRenderHostInstance(), store, logger);
|
||||||
|
_setGlobalInteractionReporter((r) => {
|
||||||
|
logger.track('usage', 'interaction', r);
|
||||||
|
if (!isProduction()) {
|
||||||
|
const msg = `[interaction] ${r.scope}:${r.action} in ${r.duration}ms`;
|
||||||
|
if (r.success) console.debug(msg);
|
||||||
|
else console.warn(msg, r.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setUserSessionManagerInstance({
|
||||||
|
internGraphPOSTAPIRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<AppFrame logger={logger} persistor={persistor} />,
|
||||||
|
document.getElementById('root'),
|
||||||
|
);
|
||||||
|
initLauncherHooks(config(), store);
|
||||||
|
enableConsoleHook();
|
||||||
|
window.flipperGlobalStoreDispatch = store.dispatch;
|
||||||
|
|
||||||
|
// listen to settings and load the right theme
|
||||||
|
sideEffect(
|
||||||
|
store,
|
||||||
|
{name: 'loadTheme', fireImmediately: false, throttleMs: 500},
|
||||||
|
(state) => state.settingsState.darkMode,
|
||||||
|
(theme) => {
|
||||||
|
let shouldUseDarkMode = false;
|
||||||
|
if (theme === 'dark') {
|
||||||
|
shouldUseDarkMode = true;
|
||||||
|
} else if (theme === 'light') {
|
||||||
|
shouldUseDarkMode = false;
|
||||||
|
} else if (theme === 'system') {
|
||||||
|
shouldUseDarkMode = getRenderHostInstance().shouldUseDarkColors();
|
||||||
|
}
|
||||||
|
(
|
||||||
|
document.getElementById('flipper-theme-import') as HTMLLinkElement
|
||||||
|
).href = `themes/${shouldUseDarkMode ? 'dark' : 'light'}.css`;
|
||||||
|
getRenderHostInstance().sendIpcEvent('setTheme', theme);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
|
// make sure all modules are loaded
|
||||||
|
// @ts-ignore
|
||||||
|
window.flipperInit = init;
|
||||||
|
window.dispatchEvent(new Event('flipper-store-ready'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const CodeBlock = styled(Input.TextArea)({
|
||||||
|
...theme.monospace,
|
||||||
|
color: theme.textColorSecondary,
|
||||||
|
});
|
||||||
@@ -13,7 +13,6 @@ import type {Store} from '../reducers';
|
|||||||
import createPaste from '../fb-stubs/createPaste';
|
import createPaste from '../fb-stubs/createPaste';
|
||||||
import GK from '../fb-stubs/GK';
|
import GK from '../fb-stubs/GK';
|
||||||
import type BaseDevice from '../devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
import {clipboard, shell} from 'electron';
|
|
||||||
import constants from '../fb-stubs/constants';
|
import constants from '../fb-stubs/constants';
|
||||||
import {addNotification} from '../reducers/notifications';
|
import {addNotification} from '../reducers/notifications';
|
||||||
import {deconstructPluginKey} from 'flipper-common';
|
import {deconstructPluginKey} from 'flipper-common';
|
||||||
@@ -49,12 +48,8 @@ export function initializeFlipperLibImplementation(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
writeTextToClipboard(text: string) {
|
writeTextToClipboard: renderHost.writeTextToClipboard,
|
||||||
clipboard.writeText(text);
|
openLink: renderHost.openLink,
|
||||||
},
|
|
||||||
openLink(url: string) {
|
|
||||||
shell.openExternal(url);
|
|
||||||
},
|
|
||||||
showNotification(pluginId, notification) {
|
showNotification(pluginId, notification) {
|
||||||
const parts = deconstructPluginKey(pluginId);
|
const parts = deconstructPluginKey(pluginId);
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
@@ -69,5 +64,9 @@ export function initializeFlipperLibImplementation(
|
|||||||
showSaveDialog: renderHost.showSaveDialog,
|
showSaveDialog: renderHost.showSaveDialog,
|
||||||
showOpenDialog: renderHost.showOpenDialog,
|
showOpenDialog: renderHost.showOpenDialog,
|
||||||
showSelectDirectoryDialog: renderHost.showSelectDirectoryDialog,
|
showSelectDirectoryDialog: renderHost.showSelectDirectoryDialog,
|
||||||
|
paths: {
|
||||||
|
appPath: renderHost.paths.appPath,
|
||||||
|
homePath: renderHost.paths.homePath,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,7 @@
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
import {remote} from 'electron';
|
|
||||||
import {getStaticPath} from './pathUtils';
|
import {getStaticPath} from './pathUtils';
|
||||||
|
|
||||||
const AVAILABLE_SIZES = [8, 10, 12, 16, 18, 20, 24, 32];
|
const AVAILABLE_SIZES = [8, 10, 12, 16, 18, 20, 24, 32];
|
||||||
@@ -85,7 +84,7 @@ export function buildIconURLSync(name: string, size: number, density: number) {
|
|||||||
) {
|
) {
|
||||||
// From utils/isProduction
|
// From utils/isProduction
|
||||||
const isProduction = !/node_modules[\\/]electron[\\/]/.test(
|
const isProduction = !/node_modules[\\/]electron[\\/]/.test(
|
||||||
process.execPath || remote.process.execPath,
|
getRenderHostInstance().paths.execPath,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isProduction) {
|
if (!isProduction) {
|
||||||
@@ -126,7 +125,12 @@ export function buildIconURLSync(name: string, size: number, density: number) {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIconURLSync(name: string, size: number, density: number) {
|
export function getIconURLSync(
|
||||||
|
name: string,
|
||||||
|
size: number,
|
||||||
|
density: number,
|
||||||
|
basePath: string = getRenderHostInstance().paths.appPath,
|
||||||
|
) {
|
||||||
if (name.indexOf('/') > -1) {
|
if (name.indexOf('/') > -1) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -161,15 +165,8 @@ export function getIconURLSync(name: string, size: number, density: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// resolve icon locally if possible
|
// resolve icon locally if possible
|
||||||
if (
|
const iconPath = path.join(basePath, buildLocalIconPath(name, size, density));
|
||||||
remote &&
|
if (fs.existsSync(iconPath)) {
|
||||||
fs.existsSync(
|
|
||||||
path.join(
|
|
||||||
remote.app.getAppPath(),
|
|
||||||
buildLocalIconPath(name, size, density),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return buildLocalIconURL(name, size, density);
|
return buildLocalIconURL(name, size, density);
|
||||||
}
|
}
|
||||||
return buildIconURLSync(name, requestedSize, density);
|
return buildIconURLSync(name, requestedSize, density);
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import electron from 'electron';
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
|
||||||
const _isProduction = !/node_modules[\\/]electron[\\/]/.test(
|
let _isProduction: boolean | undefined;
|
||||||
// We only run this once and cache the output so this slow access is okay.
|
|
||||||
// eslint-disable-next-line no-restricted-properties
|
|
||||||
process.execPath || electron.remote.process.execPath,
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function isProduction(): boolean {
|
export default function isProduction(): boolean {
|
||||||
|
if (_isProduction === undefined) {
|
||||||
|
_isProduction = !/node_modules[\\/]electron[\\/]/.test(
|
||||||
|
getRenderHostInstance().paths.execPath,
|
||||||
|
);
|
||||||
|
}
|
||||||
return _isProduction;
|
return _isProduction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,14 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import electron from 'electron';
|
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import isProduction from './isProduction';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
|
||||||
const getPackageJSON = async () => {
|
const getPackageJSON = async () => {
|
||||||
const base =
|
const base = getRenderHostInstance().paths.appPath;
|
||||||
isProduction() && electron.remote
|
|
||||||
? electron.remote.app.getAppPath()
|
|
||||||
: process.cwd();
|
|
||||||
const content = await promisify(fs.readFile)(
|
const content = await promisify(fs.readFile)(
|
||||||
path.join(base, 'package.json'),
|
path.join(base, 'package.json'),
|
||||||
'utf-8',
|
'utf-8',
|
||||||
|
|||||||
@@ -12,35 +12,15 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
// In utils this is fine when used with caching.
|
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
|
||||||
import {default as electron, remote} from 'electron';
|
|
||||||
import config from '../fb-stubs/config';
|
import config from '../fb-stubs/config';
|
||||||
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
let _staticPath = '';
|
|
||||||
|
|
||||||
function getStaticDir() {
|
|
||||||
if (_staticPath) {
|
|
||||||
return _staticPath;
|
|
||||||
}
|
|
||||||
_staticPath = path.resolve(__dirname, '..', '..', '..', 'static');
|
|
||||||
if (fs.existsSync(_staticPath)) {
|
|
||||||
return _staticPath;
|
|
||||||
}
|
|
||||||
if (remote && fs.existsSync(remote.app.getAppPath())) {
|
|
||||||
_staticPath = path.join(remote.app.getAppPath());
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(_staticPath)) {
|
|
||||||
throw new Error('Static path does not exist: ' + _staticPath);
|
|
||||||
}
|
|
||||||
return _staticPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStaticPath(
|
export function getStaticPath(
|
||||||
relativePath: string = '.',
|
relativePath: string = '.',
|
||||||
{asarUnpacked}: {asarUnpacked: boolean} = {asarUnpacked: false},
|
{asarUnpacked}: {asarUnpacked: boolean} = {asarUnpacked: false},
|
||||||
) {
|
) {
|
||||||
const staticDir = getStaticDir();
|
const staticDir = getRenderHostInstance().paths.staticPath;
|
||||||
const absolutePath = path.resolve(staticDir, relativePath);
|
const absolutePath = path.resolve(staticDir, relativePath);
|
||||||
// Unfortunately, path.resolve, fs.pathExists, fs.read etc do not automatically work with asarUnpacked files.
|
// Unfortunately, path.resolve, fs.pathExists, fs.read etc do not automatically work with asarUnpacked files.
|
||||||
// All these functions still look for files in "app.asar" even if they are unpacked.
|
// All these functions still look for files in "app.asar" even if they are unpacked.
|
||||||
@@ -51,26 +31,6 @@ export function getStaticPath(
|
|||||||
: absolutePath;
|
: absolutePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _appPath: string | undefined = undefined;
|
|
||||||
export function getAppPath() {
|
|
||||||
if (!_appPath) {
|
|
||||||
_appPath = getStaticPath('..');
|
|
||||||
}
|
|
||||||
|
|
||||||
return _appPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _tempPath: string | undefined = undefined;
|
|
||||||
export function getAppTempPath() {
|
|
||||||
if (!_tempPath) {
|
|
||||||
// We cache this.
|
|
||||||
// eslint-disable-next-line no-restricted-properties
|
|
||||||
_tempPath = (electron.app || electron.remote.app).getPath('temp');
|
|
||||||
}
|
|
||||||
|
|
||||||
return _tempPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getChangelogPath() {
|
export function getChangelogPath() {
|
||||||
const changelogPath = getStaticPath(config.isFBBuild ? 'facebook' : '.');
|
const changelogPath = getStaticPath(config.isFBBuild ? 'facebook' : '.');
|
||||||
if (fs.existsSync(changelogPath)) {
|
if (fs.existsSync(changelogPath)) {
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
import {remote} from 'electron';
|
|
||||||
|
|
||||||
export type ProcessConfig = {
|
export type ProcessConfig = {
|
||||||
disabledPlugins: Set<string>;
|
disabledPlugins: Set<string>;
|
||||||
@@ -27,9 +26,7 @@ export type ProcessConfig = {
|
|||||||
let configObj: ProcessConfig | null = null;
|
let configObj: ProcessConfig | null = null;
|
||||||
export default function config(): ProcessConfig {
|
export default function config(): ProcessConfig {
|
||||||
if (configObj === null) {
|
if (configObj === null) {
|
||||||
const json = JSON.parse(
|
const json = JSON.parse(getRenderHostInstance().env.CONFIG || '{}');
|
||||||
(remote && remote.process.env.CONFIG) || process.env.CONFIG || '{}',
|
|
||||||
);
|
|
||||||
configObj = {
|
configObj = {
|
||||||
disabledPlugins: new Set(json.disabledPlugins || []),
|
disabledPlugins: new Set(json.disabledPlugins || []),
|
||||||
lastWindowPosition: json.lastWindowPosition,
|
lastWindowPosition: json.lastWindowPosition,
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import path from 'path';
|
|||||||
import BaseDevice from '../devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
import {reportPlatformFailures} from 'flipper-common';
|
import {reportPlatformFailures} from 'flipper-common';
|
||||||
import expandTilde from 'expand-tilde';
|
import expandTilde from 'expand-tilde';
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
|
||||||
import {remote} from 'electron';
|
|
||||||
import config from '../utils/processConfig';
|
import config from '../utils/processConfig';
|
||||||
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
|
||||||
// TODO: refactor so this doesn't need to be exported
|
export function getCaptureLocation() {
|
||||||
export const CAPTURE_LOCATION = expandTilde(
|
return expandTilde(
|
||||||
config().screenCapturePath || remote.app.getPath('desktop'),
|
config().screenCapturePath || getRenderHostInstance().paths.desktopPath,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: refactor so this doesn't need to be exported
|
// TODO: refactor so this doesn't need to be exported
|
||||||
export function getFileName(extension: 'png' | 'mp4'): string {
|
export function getFileName(extension: 'png' | 'mp4'): string {
|
||||||
@@ -32,7 +32,7 @@ export async function capture(device: BaseDevice): Promise<string> {
|
|||||||
console.log('Skipping screenshot for disconnected device');
|
console.log('Skipping screenshot for disconnected device');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const pngPath = path.join(CAPTURE_LOCATION, getFileName('png'));
|
const pngPath = path.join(getCaptureLocation(), getFileName('png'));
|
||||||
return reportPlatformFailures(
|
return reportPlatformFailures(
|
||||||
device.screenshot().then((buffer) => writeBufferToFile(pngPath, buffer)),
|
device.screenshot().then((buffer) => writeBufferToFile(pngPath, buffer)),
|
||||||
'captureScreenshot',
|
'captureScreenshot',
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ export interface FlipperLib {
|
|||||||
};
|
};
|
||||||
}): Promise<string | undefined>;
|
}): Promise<string | undefined>;
|
||||||
showSelectDirectoryDialog?(defaultPath?: string): Promise<string | undefined>;
|
showSelectDirectoryDialog?(defaultPath?: string): Promise<string | undefined>;
|
||||||
|
paths: {
|
||||||
|
homePath: string;
|
||||||
|
appPath: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export let flipperLibInstance: FlipperLib | undefined;
|
export let flipperLibInstance: FlipperLib | undefined;
|
||||||
|
|||||||
@@ -379,6 +379,10 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
|
|||||||
writeTextToClipboard: jest.fn(),
|
writeTextToClipboard: jest.fn(),
|
||||||
openLink: jest.fn(),
|
openLink: jest.fn(),
|
||||||
showNotification: jest.fn(),
|
showNotification: jest.fn(),
|
||||||
|
paths: {
|
||||||
|
appPath: process.cwd(),
|
||||||
|
homePath: `/dev/null`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {styled, colors, FlexColumn} from 'flipper';
|
import {styled, colors, FlexColumn} from 'flipper';
|
||||||
|
|
||||||
import electron from 'electron';
|
|
||||||
|
|
||||||
const devToolsNodeId = (url: string) =>
|
const devToolsNodeId = (url: string) =>
|
||||||
`hermes-chromedevtools-out-of-react-node-${url.replace(/\W+/g, '-')}`;
|
`hermes-chromedevtools-out-of-react-node-${url.replace(/\W+/g, '-')}`;
|
||||||
|
|
||||||
@@ -28,10 +26,16 @@ function createDevToolsNode(
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is necessary to activate chrome devtools in electron
|
// It is necessary to deactivate chrome devtools in electron
|
||||||
electron.remote.getCurrentWindow().webContents.toggleDevTools();
|
try {
|
||||||
electron.remote.getCurrentWindow().webContents.closeDevTools();
|
const electron = require('electron');
|
||||||
|
if (electron.default) {
|
||||||
|
electron.default.remote.getCurrentWindow().webContents.toggleDevTools();
|
||||||
|
electron.default.remote.getCurrentWindow().webContents.closeDevTools();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to close Electron devtools: ', e);
|
||||||
|
}
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
wrapper.id = devToolsNodeId(url);
|
wrapper.id = devToolsNodeId(url);
|
||||||
wrapper.style.height = '100%';
|
wrapper.style.height = '100%';
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {getAppPath} from 'flipper';
|
|
||||||
import {AppMatchPattern} from '../types';
|
import {AppMatchPattern} from '../types';
|
||||||
import {Device} from 'flipper-plugin';
|
import {Device, getFlipperLib} from 'flipper-plugin';
|
||||||
|
|
||||||
let patternsPath: string | undefined;
|
let patternsPath: string | undefined;
|
||||||
|
|
||||||
function getPatternsBasePath() {
|
function getPatternsBasePath() {
|
||||||
return (patternsPath = patternsPath ?? path.join(getAppPath(), 'facebook'));
|
return (patternsPath =
|
||||||
|
patternsPath ?? path.join(getFlipperLib().paths.appPath, 'facebook'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractAppNameFromSelectedApp = (selectedApp: string | null) => {
|
const extractAppNameFromSelectedApp = (selectedApp: string | null) => {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
getIconsSync,
|
getIconsSync,
|
||||||
buildLocalIconPath,
|
buildLocalIconPath,
|
||||||
getIconURLSync,
|
getIconURLSync,
|
||||||
|
Icons,
|
||||||
} from '../app/src/utils/icons';
|
} from '../app/src/utils/icons';
|
||||||
import isFB from './isFB';
|
import isFB from './isFB';
|
||||||
import copyPackageWithDependencies from './copy-package-with-dependencies';
|
import copyPackageWithDependencies from './copy-package-with-dependencies';
|
||||||
@@ -309,7 +310,12 @@ async function copyStaticFolder(buildFolder: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function downloadIcons(buildFolder: string) {
|
function downloadIcons(buildFolder: string) {
|
||||||
const iconURLs = Object.entries(getIconsSync()).reduce<
|
const icons: Icons = JSON.parse(
|
||||||
|
fs.readFileSync(path.join(buildFolder, 'icons.json'), {
|
||||||
|
encoding: 'utf8',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const iconURLs = Object.entries(icons).reduce<
|
||||||
{
|
{
|
||||||
name: string;
|
name: string;
|
||||||
size: number;
|
size: number;
|
||||||
@@ -326,7 +332,7 @@ function downloadIcons(buildFolder: string) {
|
|||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
iconURLs.map(({name, size, density}) => {
|
iconURLs.map(({name, size, density}) => {
|
||||||
const url = getIconURLSync(name, size, density);
|
const url = getIconURLSync(name, size, density, buildFolder);
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
retryOptions: {
|
retryOptions: {
|
||||||
// Be default, only 5xx are retried but we're getting the odd 404
|
// Be default, only 5xx are retried but we're getting the odd 404
|
||||||
|
|||||||
Reference in New Issue
Block a user