move node types, stub node modules

Summary: This diff stubs all node modules when running in the browser, so that, albeit with a lot of errors and without plugins, the UI loads in a browser. To be continued in the rest of this diff

Reviewed By: antonk52

Differential Revision: D32665705

fbshipit-source-id: 4632e241f59c5b9712a41d01a26878afb01f69b5
This commit is contained in:
Michel Weststrate
2021-12-08 04:25:28 -08:00
committed by Facebook GitHub Bot
parent d0402d7268
commit ad4a55f263
20 changed files with 137 additions and 50 deletions

View File

@@ -24,6 +24,7 @@
},
"optionalDependencies": {},
"devDependencies": {
"@types/node": "^15.12.5",
"react-refresh": "^0.11.0"
}
}

View File

@@ -179,7 +179,7 @@ export function initializeElectron(
},
flipperServer,
async requirePlugin(path) {
return (window as any).electronRequire(path);
return global.electronRequire(path);
},
getStaticResourceUrl(relativePath): string {
return (

View File

@@ -39,7 +39,9 @@
"ws": "^8.2.3",
"xdg-basedir": "^4.0.0"
},
"devDependencies": {},
"devDependencies": {
"@types/node": "^15.12.5"
},
"peerDependencies": {},
"scripts": {
"reset": "rimraf lib *.tsbuildinfo",

View File

@@ -45,7 +45,9 @@ const getTempDirName = promisify(tmp.dir) as (
export class PluginManager {
async start() {
// This needn't happen immediately and is (light) I/O work.
(window.requestIdleCallback || setImmediate)(() => {
(typeof window !== 'undefined'
? window?.requestIdleCallback
: setImmediate)(() => {
cleanupOldInstalledPluginVersions(maxInstalledPluginVersionsToKeep).catch(
(err) =>
console.error('Failed to clean up old installed plugins:', err),

View File

@@ -22,6 +22,7 @@
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/node": "^15.12.5",
"metro": "^0.66.2",
"nodemon": "^2.0.15",
"ts-node": "^9.1.1",
@@ -32,7 +33,7 @@
"reset": "rimraf lib *.tsbuildinfo",
"build": "tsc -b",
"prepack": "yarn reset && yarn build",
"start": "cross-env NODE_ENV=development nodemon --watch './src/**/*.tsx' --watch '../flipper-server-core/src/**/*.tsx' --exec 'yarn build && ../ts-node src/index.tsx'"
"start": "cross-env NODE_ENV=development nodemon --ext 'tsx' --watch './src/' --watch '../flipper-server-core/src/' --exec 'yarn build && ../ts-node src/index.tsx'"
},
"files": [
"lib/**/*"

View File

@@ -39,7 +39,8 @@ async function start() {
start()
.then(() => {
console.log(
`Flipper DEV server started at http://localhost:${PORT}/index.web.dev.html`,
'Flipper DEV server started at ' +
chalk.green(chalk.bold(`http://localhost:${PORT}/index.web.dev.html`)),
);
})
.catch((e) => {

View File

@@ -16,6 +16,9 @@ import socketio from 'socket.io';
import {getWatchFolders} from 'flipper-pkg-lib';
import Metro from 'metro';
import pFilter from 'p-filter';
// provided by Metro
// eslint-disable-next-line
import MetroResolver from 'metro-resolver';
const uiSourceDirs = [
'flipper-ui-browser',
@@ -24,6 +27,53 @@ const uiSourceDirs = [
'flipper-common',
];
const stubModules = new Set([
'fs',
'path',
'crypto',
'process',
'os',
'util',
'child_process',
'assert',
'adbkit', // TODO: factor out!
'zlib',
'events',
'fs-extra',
'archiver',
'graceful-fs',
'stream',
'url',
'node-fetch',
'net',
'vm',
'debug',
'lockfile',
'constants',
'https',
'plugin-lib', // TODO: we only want the types?
'flipper-plugin-lib',
'tar',
'minipass',
'live-plugin-manager',
'decompress-tar',
'readable-stream',
'archiver-utils',
'metro',
'decompress',
'temp',
'tmp',
'promisify-child-process',
'jsdom',
'extract-zip',
'yauzl',
'fd-slicer',
'envinfo',
'bser',
'fb-watchman',
// TODO fix me
]);
// This file is heavily inspired by scripts/start-dev-server.ts!
export async function startWebServerDev(
app: Express,
@@ -84,8 +134,27 @@ async function startMetroServer(
resolver: {
...baseConfig.resolver,
resolverMainFields: ['flipperBundlerEntry', 'browser', 'module', 'main'],
blacklistRE: /\.native\.js$/,
blacklistRE: [/\.native\.js$/],
sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs', 'cjs'],
resolveRequest(context: any, moduleName: string, ...rest: any[]) {
if (stubModules.has(moduleName)) {
// console.warn("Found reference to ", moduleName)
return {
type: 'empty',
};
}
// if (moduleName.includes('pluginPaths')) {
// console.error('got ' + moduleName, rest);
// }
return MetroResolver.resolve(
{
...context,
resolveRequest: null,
},
moduleName,
...rest,
);
},
},
watch: true,
// only needed when medling with babel transforms

View File

@@ -7,7 +7,7 @@
* @format
*/
import {RenderHost} from 'flipper-ui-core';
import type {RenderHost} from 'flipper-ui-core';
declare global {
interface Window {

View File

@@ -14,6 +14,13 @@ import {createFlipperServer} from './flipperServerConnection';
document.getElementById('root')!.innerText = 'flipper-ui-browser started';
async function start() {
(global as any).electronRequire = function (path: string) {
console.error(
`[decapitate] Tried to electronRequire ${path}, this module is not available in the browser and will be stubbed`,
);
return {};
};
const logger = createDelegatedLogger();
setLoggerInstance(logger);
@@ -28,10 +35,11 @@ async function start() {
// 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
// TODO: remove
window.flipperShowError?.('Connected to Flipper Server successfully');
// TODO: require('flipper-ui-core').startFlipperDesktop(flipperServer);
// eslint-disable-next-line import/no-commonjs
require('flipper-ui-core').startFlipperDesktop(flipperServer);
window.flipperHideError?.();
}
start().catch((e) => {

View File

@@ -8,7 +8,7 @@
*/
import {FlipperServer, FlipperServerConfig} from 'flipper-common';
import {getRenderHostInstance, RenderHost} from 'flipper-ui-core';
import {RenderHost} from 'flipper-ui-core';
export function initializeRenderHost(
flipperServer: FlipperServer,
@@ -63,10 +63,7 @@ export function initializeRenderHost(
flipperServer,
async requirePlugin(path) {
// TODO: use `await import(path)`?
const source = await getRenderHostInstance().flipperServer.exec(
'plugin-source',
path,
);
const source = await flipperServer.exec('plugin-source', path);
// eslint-disable-next-line no-eval
return eval(source);
},

View File

@@ -18,7 +18,6 @@
"@types/archiver": "^5.1.1",
"@types/uuid": "^8.3.1",
"adbkit": "^2.11.1",
"adbkit-logcat": "^2.0.1",
"antd": "4.16.8",
"archiver": "^5.0.2",
"async-mutex": "^0.3.2",

View File

@@ -20,10 +20,6 @@ declare global {
| undefined
| (StoreEnhancerStoreCreator & StoreEnhancerStateSanitizer);
Flipper: {
init: () => void;
};
FlipperRenderHostInstance: RenderHost;
}
}

View File

@@ -9,13 +9,14 @@
import util from 'util';
import {exec as execImport} from 'child_process';
const exec = util.promisify(execImport);
const cmd = 'klist --json';
const endWith = '@THEFACEBOOK.COM';
export async function isFBEmployee(): Promise<boolean> {
return exec(cmd).then(
return util
.promisify(execImport)(cmd)
.then(
(stdobj: {stderr: string; stdout: string}) => {
const principal = String(JSON.parse(stdobj.stdout).principal);

View File

@@ -95,7 +95,6 @@
"@types/lodash.debounce": "^4.0.6",
"@types/mkdirp": "^1.0.2",
"@types/mock-fs": "^4.13.1",
"@types/node": "^15.12.5",
"@types/npm-packlist": "^1.1.2",
"@types/promise-retry": "^1.1.3",
"@types/react": "17.0.34",
@@ -234,7 +233,7 @@
"fix": "eslint . --fix --ext .js,.ts,.tsx",
"lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins",
"lint:eslint": "eslint . --ext .js,.ts,.tsx",
"lint:tsc": "tsc --noemit",
"lint:tsc": "tsc -p tsc-root/tsconfig.json --noemit",
"list-plugins": "./ts-node scripts/list-plugins.ts",
"open-dist": "open ../dist/mac/Flipper.app --args --launcher=false --inspect=9229",
"postinstall": "patch-package && ./ts-node scripts/gen-type-index.ts && yarn --cwd plugins install --mutex network:30331 && yarn build:tsc && ./ts-node scripts/generate-plugin-entry-points.ts && yarn build:themes",

View File

@@ -32,8 +32,6 @@ import pmap from 'p-map';
import semver from 'semver';
import {notNull} from './typeUtils';
const getTmpDir = promisify(tmp.dir) as () => Promise<string>;
function providePluginManagerNoDependencies(): PM {
return new PM({ignoredDependencies: [/.*/]});
}
@@ -43,7 +41,7 @@ async function installPluginFromTempDir(
): Promise<InstalledPluginDetails> {
const pluginDetails = await getInstalledPluginDetails(sourceDir);
const {name, version} = pluginDetails;
const backupDir = path.join(await getTmpDir(), `${name}-${version}`);
const backupDir = path.join(await promisify(tmp.dir)(), `${name}-${version}`);
const destinationDir = getPluginVersionInstallationDir(name, version);
if (pluginDetails.specVersion == 1) {
@@ -99,7 +97,7 @@ export async function getInstalledPlugin(
}
export async function installPluginFromNpm(name: string) {
const tmpDir = await getTmpDir();
const tmpDir = await promisify(tmp.dir)();
try {
await fs.ensureDir(tmpDir);
const plugManNoDep = providePluginManagerNoDependencies();
@@ -118,7 +116,7 @@ export async function installPluginFromNpm(name: string) {
export async function installPluginFromFile(
packagePath: string,
): Promise<InstalledPluginDetails> {
const tmpDir = await getTmpDir();
const tmpDir = await promisify(tmp.dir)();
try {
const files = await decompress(packagePath, tmpDir, {
plugins: [decompressTargz(), decompressUnzip()],

View File

@@ -92,7 +92,9 @@ export function devicePlugin(client: DevicePluginClient) {
let metroReloadAttempts = 0;
function getGlobalDevToolsModule(): ReactDevToolsStandaloneType {
const required = global.electronRequire(globalDevToolsPath.get()!).default;
const required = (global as any).electronRequire(
globalDevToolsPath.get()!,
).default;
return required.default ?? required;
}

View File

@@ -63,11 +63,6 @@
(function() {
// FIXME: needed to make Metro work
window.global = window;
global.electronRequire = function(path) {
console.error("[decapitate] Tried to electronRequire: " + path);
return {};
};
let suppressErrors = false;
const socket = io(location.origin);
@@ -81,6 +76,7 @@
suppressErrors = true;
});
function openError(text) {
if (suppressErrors) {
return;
@@ -89,6 +85,7 @@
const box = document.querySelector('.__infinity-dev-box-error');
box.removeAttribute('hidden');
box.textContent = text;
box.appendChild(closeButton);
}
window.flipperShowError = openError;
window.flipperHideError = () => {
@@ -96,6 +93,10 @@
box.setAttribute('hidden', true);
}
const closeButton = document.createElement('button');
closeButton.addEventListener('click', window.flipperHideError);
closeButton.textContent = 'X';
// load correct theme (n.b. this doesn't handle system value specifically, will assume light in such cases)
try {
if (window.flipperConfig.theme === 'dark') {

16
desktop/types/flipperGlobals.d.ts vendored Normal file
View 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 const __REVISION__: string | undefined;
declare const __VERSION__: string;
declare const electronRequire: {
(name: string): any;
resolve: (module: string) => string;
cache: {[module: string]: any};
};

View File

@@ -16,6 +16,7 @@
/// <reference path="decompress-targz.d.ts" />
/// <reference path="decompress-unzip.d.ts" />
/// <reference path="download-tarball.d.ts" />
/// <reference path="flipperGlobals.d.ts" />
/// <reference path="jest-extensions.d.ts" />
/// <reference path="json-format-highlight.d.ts" />
/// <reference path="line-replace.d.ts" />

View File

@@ -9,13 +9,6 @@
declare module NodeJS {
interface Global {
__REVISION__: string | undefined;
__VERSION__: string;
electronRequire: {
(name: string): any;
resolve: (module: string) => string;
cache: {[module: string]: any};
};
window: Window | undefined;
WebSocket: any;
fetch: any;