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": {}, "optionalDependencies": {},
"devDependencies": { "devDependencies": {
"@types/node": "^15.12.5",
"react-refresh": "^0.11.0" "react-refresh": "^0.11.0"
} }
} }

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/node": "^15.12.5",
"metro": "^0.66.2", "metro": "^0.66.2",
"nodemon": "^2.0.15", "nodemon": "^2.0.15",
"ts-node": "^9.1.1", "ts-node": "^9.1.1",
@@ -32,7 +33,7 @@
"reset": "rimraf lib *.tsbuildinfo", "reset": "rimraf lib *.tsbuildinfo",
"build": "tsc -b", "build": "tsc -b",
"prepack": "yarn reset && yarn build", "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": [ "files": [
"lib/**/*" "lib/**/*"

View File

@@ -39,7 +39,8 @@ async function start() {
start() start()
.then(() => { .then(() => {
console.log( 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) => { .catch((e) => {

View File

@@ -16,6 +16,9 @@ import socketio from 'socket.io';
import {getWatchFolders} from 'flipper-pkg-lib'; import {getWatchFolders} from 'flipper-pkg-lib';
import Metro from 'metro'; import Metro from 'metro';
import pFilter from 'p-filter'; import pFilter from 'p-filter';
// provided by Metro
// eslint-disable-next-line
import MetroResolver from 'metro-resolver';
const uiSourceDirs = [ const uiSourceDirs = [
'flipper-ui-browser', 'flipper-ui-browser',
@@ -24,6 +27,53 @@ const uiSourceDirs = [
'flipper-common', '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! // This file is heavily inspired by scripts/start-dev-server.ts!
export async function startWebServerDev( export async function startWebServerDev(
app: Express, app: Express,
@@ -84,8 +134,27 @@ async function startMetroServer(
resolver: { resolver: {
...baseConfig.resolver, ...baseConfig.resolver,
resolverMainFields: ['flipperBundlerEntry', 'browser', 'module', 'main'], resolverMainFields: ['flipperBundlerEntry', 'browser', 'module', 'main'],
blacklistRE: /\.native\.js$/, blacklistRE: [/\.native\.js$/],
sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs', 'cjs'], 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, watch: true,
// only needed when medling with babel transforms // only needed when medling with babel transforms

View File

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

View File

@@ -14,6 +14,13 @@ import {createFlipperServer} from './flipperServerConnection';
document.getElementById('root')!.innerText = 'flipper-ui-browser started'; document.getElementById('root')!.innerText = 'flipper-ui-browser started';
async function start() { 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(); const logger = createDelegatedLogger();
setLoggerInstance(logger); setLoggerInstance(logger);
@@ -28,10 +35,11 @@ async function start() {
// before starting the rest of the Flipper process. // before starting the rest of the Flipper process.
// This prevent issues where the render host is referred at module initialisation level, // This prevent issues where the render host is referred at module initialisation level,
// but not set yet, which might happen when using normal imports. // but not set yet, which might happen when using normal imports.
// eslint-disable-next-line import/no-commonjs // TODO: remove
// TODO: replace
window.flipperShowError?.('Connected to Flipper Server successfully'); 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) => { start().catch((e) => {

View File

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

View File

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

View File

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

View File

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

View File

@@ -95,7 +95,6 @@
"@types/lodash.debounce": "^4.0.6", "@types/lodash.debounce": "^4.0.6",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^15.12.5",
"@types/npm-packlist": "^1.1.2", "@types/npm-packlist": "^1.1.2",
"@types/promise-retry": "^1.1.3", "@types/promise-retry": "^1.1.3",
"@types/react": "17.0.34", "@types/react": "17.0.34",
@@ -234,7 +233,7 @@
"fix": "eslint . --fix --ext .js,.ts,.tsx", "fix": "eslint . --fix --ext .js,.ts,.tsx",
"lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins", "lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins",
"lint:eslint": "eslint . --ext .js,.ts,.tsx", "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", "list-plugins": "./ts-node scripts/list-plugins.ts",
"open-dist": "open ../dist/mac/Flipper.app --args --launcher=false --inspect=9229", "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", "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 semver from 'semver';
import {notNull} from './typeUtils'; import {notNull} from './typeUtils';
const getTmpDir = promisify(tmp.dir) as () => Promise<string>;
function providePluginManagerNoDependencies(): PM { function providePluginManagerNoDependencies(): PM {
return new PM({ignoredDependencies: [/.*/]}); return new PM({ignoredDependencies: [/.*/]});
} }
@@ -43,7 +41,7 @@ async function installPluginFromTempDir(
): Promise<InstalledPluginDetails> { ): Promise<InstalledPluginDetails> {
const pluginDetails = await getInstalledPluginDetails(sourceDir); const pluginDetails = await getInstalledPluginDetails(sourceDir);
const {name, version} = pluginDetails; 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); const destinationDir = getPluginVersionInstallationDir(name, version);
if (pluginDetails.specVersion == 1) { if (pluginDetails.specVersion == 1) {
@@ -99,7 +97,7 @@ export async function getInstalledPlugin(
} }
export async function installPluginFromNpm(name: string) { export async function installPluginFromNpm(name: string) {
const tmpDir = await getTmpDir(); const tmpDir = await promisify(tmp.dir)();
try { try {
await fs.ensureDir(tmpDir); await fs.ensureDir(tmpDir);
const plugManNoDep = providePluginManagerNoDependencies(); const plugManNoDep = providePluginManagerNoDependencies();
@@ -118,7 +116,7 @@ export async function installPluginFromNpm(name: string) {
export async function installPluginFromFile( export async function installPluginFromFile(
packagePath: string, packagePath: string,
): Promise<InstalledPluginDetails> { ): Promise<InstalledPluginDetails> {
const tmpDir = await getTmpDir(); const tmpDir = await promisify(tmp.dir)();
try { try {
const files = await decompress(packagePath, tmpDir, { const files = await decompress(packagePath, tmpDir, {
plugins: [decompressTargz(), decompressUnzip()], plugins: [decompressTargz(), decompressUnzip()],

View File

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

View File

@@ -63,11 +63,6 @@
(function() { (function() {
// FIXME: needed to make Metro work // FIXME: needed to make Metro work
window.global = window; window.global = window;
global.electronRequire = function(path) {
console.error("[decapitate] Tried to electronRequire: " + path);
return {};
};
let suppressErrors = false; let suppressErrors = false;
const socket = io(location.origin); const socket = io(location.origin);
@@ -81,6 +76,7 @@
suppressErrors = true; suppressErrors = true;
}); });
function openError(text) { function openError(text) {
if (suppressErrors) { if (suppressErrors) {
return; return;
@@ -89,6 +85,7 @@
const box = document.querySelector('.__infinity-dev-box-error'); const box = document.querySelector('.__infinity-dev-box-error');
box.removeAttribute('hidden'); box.removeAttribute('hidden');
box.textContent = text; box.textContent = text;
box.appendChild(closeButton);
} }
window.flipperShowError = openError; window.flipperShowError = openError;
window.flipperHideError = () => { window.flipperHideError = () => {
@@ -96,6 +93,10 @@
box.setAttribute('hidden', true); 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) // load correct theme (n.b. this doesn't handle system value specifically, will assume light in such cases)
try { try {
if (window.flipperConfig.theme === 'dark') { 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-targz.d.ts" />
/// <reference path="decompress-unzip.d.ts" /> /// <reference path="decompress-unzip.d.ts" />
/// <reference path="download-tarball.d.ts" /> /// <reference path="download-tarball.d.ts" />
/// <reference path="flipperGlobals.d.ts" />
/// <reference path="jest-extensions.d.ts" /> /// <reference path="jest-extensions.d.ts" />
/// <reference path="json-format-highlight.d.ts" /> /// <reference path="json-format-highlight.d.ts" />
/// <reference path="line-replace.d.ts" /> /// <reference path="line-replace.d.ts" />

View File

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