Remove dynamic dependencies from flipper-server
Summary: Currently, Flipper Server has a few productions dependencies (mac-ca, node-fetch) that are not bundled with the Flipper Server. It makes it harder to distribute Flipper Server, as now all potential consumers need not only to download the bundle, but also install these additional dependencies. This diff makes it possible to bundle `mac-ca` and `node-fetch` with Flipper Server. As a result, Flipper Server becomes dependency-free in production. Reviewed By: lblasa Differential Revision: D36345213 fbshipit-source-id: 2cd6ba1b3301b45dc2295891964ba020fd107586
This commit is contained in:
committed by
Facebook GitHub Bot
parent
21dfeca756
commit
d1ed676a48
@@ -41,15 +41,6 @@ import fs from 'fs-extra';
|
|||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') {
|
|
||||||
// By default Node.JS has its internal certificate storage and doesn't use
|
|
||||||
// the system store. Because of this, it's impossible to access ondemand / devserver
|
|
||||||
// which are signed using some internal self-issued FB certificates. These certificates
|
|
||||||
// are automatically installed to MacOS system store on FB machines, so here we're using
|
|
||||||
// this "mac-ca" library to load them into Node.JS.
|
|
||||||
electronRequire('mac-ca');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
const app = remote.app;
|
const app = remote.app;
|
||||||
const execPath = process.execPath || remote.process.execPath;
|
const execPath = process.execPath || remote.process.execPath;
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ export const BUILTINS = [
|
|||||||
'repl',
|
'repl',
|
||||||
'timers',
|
'timers',
|
||||||
'perf_hooks',
|
'perf_hooks',
|
||||||
|
'worker_threads',
|
||||||
|
'encoding',
|
||||||
'fsevents',
|
'fsevents',
|
||||||
'./fsevents.node',
|
'./fsevents.node',
|
||||||
// MWE node-fetch looks strange here, not sure what the effect of changing that would be
|
|
||||||
'node-fetch',
|
|
||||||
// jest is referred to in source code, like in TestUtils, but we don't want to ever bundle it up!
|
// jest is referred to in source code, like in TestUtils, but we don't want to ever bundle it up!
|
||||||
'jest',
|
'jest',
|
||||||
'@testing-library/react',
|
'@testing-library/react',
|
||||||
|
|||||||
40
desktop/babel-transformer/src/prefixed-node-requires.tsx
Normal file
40
desktop/babel-transformer/src/prefixed-node-requires.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and 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 {CallExpression} from '@babel/types';
|
||||||
|
import {NodePath} from '@babel/traverse';
|
||||||
|
|
||||||
|
// Core modules can be required as `node:fs` to bypass teh require cache
|
||||||
|
// https://nodejs.org/api/modules.html#core-modules
|
||||||
|
// It is not supported for "require" until Node v14.18.
|
||||||
|
// TODO: Remove this transform when we upgrade electron and node.js
|
||||||
|
const bypassRequireCachePrefix = 'node:';
|
||||||
|
|
||||||
|
module.exports = () => ({
|
||||||
|
name: 'change-require-to-electronRequire-in-electron-app',
|
||||||
|
visitor: {
|
||||||
|
CallExpression(path: NodePath<CallExpression>) {
|
||||||
|
const node = path.node;
|
||||||
|
if (
|
||||||
|
node.type === 'CallExpression' &&
|
||||||
|
node.callee.type === 'Identifier' &&
|
||||||
|
node.callee.name === 'require' &&
|
||||||
|
node.arguments.length === 1 &&
|
||||||
|
node.arguments[0].type === 'StringLiteral'
|
||||||
|
) {
|
||||||
|
const source = node.arguments[0].value;
|
||||||
|
if (source.startsWith(bypassRequireCachePrefix)) {
|
||||||
|
node.arguments[0].value = source.substring(
|
||||||
|
bypassRequireCachePrefix.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -13,6 +13,7 @@ import {default as getCacheKey} from './get-cache-key';
|
|||||||
const presets = [require('@babel/preset-react')];
|
const presets = [require('@babel/preset-react')];
|
||||||
const plugins = [
|
const plugins = [
|
||||||
require('./fsevents-dynamic-imports'),
|
require('./fsevents-dynamic-imports'),
|
||||||
|
require('./prefixed-node-requires'),
|
||||||
require('./electron-requires'),
|
require('./electron-requires'),
|
||||||
require('./import-react'),
|
require('./import-react'),
|
||||||
require('./app-flipper-requires'),
|
require('./app-flipper-requires'),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {default as doTransform} from './transform';
|
|||||||
const presets = [require('@babel/preset-react')];
|
const presets = [require('@babel/preset-react')];
|
||||||
const plugins = [
|
const plugins = [
|
||||||
require('./fsevents-dynamic-imports'),
|
require('./fsevents-dynamic-imports'),
|
||||||
|
require('./prefixed-node-requires'),
|
||||||
require('./electron-requires'),
|
require('./electron-requires'),
|
||||||
require('./plugin-flipper-requires'),
|
require('./plugin-flipper-requires'),
|
||||||
require('./fb-stubs'),
|
require('./fb-stubs'),
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const presets = [
|
|||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
require('./fsevents-dynamic-imports'),
|
require('./fsevents-dynamic-imports'),
|
||||||
|
require('./prefixed-node-requires'),
|
||||||
require('./electron-requires'),
|
require('./electron-requires'),
|
||||||
require('./plugin-flipper-requires'),
|
require('./plugin-flipper-requires'),
|
||||||
require('./fb-stubs'),
|
require('./fb-stubs'),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const presets = [
|
|||||||
// (which effectively makes them external, as electronRequire === require, but not rolled up with Metro)
|
// (which effectively makes them external, as electronRequire === require, but not rolled up with Metro)
|
||||||
const plugins = [
|
const plugins = [
|
||||||
require('./fsevents-dynamic-imports'),
|
require('./fsevents-dynamic-imports'),
|
||||||
|
require('./prefixed-node-requires'),
|
||||||
require('./electron-requires'),
|
require('./electron-requires'),
|
||||||
require('./plugin-flipper-requires'),
|
require('./plugin-flipper-requires'),
|
||||||
require('./fb-stubs'),
|
require('./fb-stubs'),
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.7.2",
|
||||||
"lodash.memoize": "^4.1.2",
|
"lodash.memoize": "^4.1.2",
|
||||||
"node-fetch": "^3.2.4",
|
"node-fetch": "^3.2.4",
|
||||||
|
"node-forge": "^0.10.0",
|
||||||
"open": "^8.3.0",
|
"open": "^8.3.0",
|
||||||
"openssl-wrapper": "^0.3.4",
|
"openssl-wrapper": "^0.3.4",
|
||||||
"promisify-child-process": "^4.1.1",
|
"promisify-child-process": "^4.1.1",
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
"@types/invariant": "^2.2.35",
|
"@types/invariant": "^2.2.35",
|
||||||
"@types/memorystream": "^0.3.0",
|
"@types/memorystream": "^0.3.0",
|
||||||
"@types/node": "^17.0.31",
|
"@types/node": "^17.0.31",
|
||||||
|
"@types/node-forge": "^0.10",
|
||||||
"@types/rimraf": "^3.0.2",
|
"@types/rimraf": "^3.0.2",
|
||||||
"@types/rsocket-core": "^0.0.7",
|
"@types/rsocket-core": "^0.0.7",
|
||||||
"@types/rsocket-tcp-server": "^0.0.2",
|
"@types/rsocket-tcp-server": "^0.0.2",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import './utils/macCa';
|
||||||
import './utils/fetch-polyfill';
|
import './utils/fetch-polyfill';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import {ServerController} from './comms/ServerController';
|
import {ServerController} from './comms/ServerController';
|
||||||
|
|||||||
128
desktop/flipper-server-core/src/utils/macCa.tsx
Normal file
128
desktop/flipper-server-core/src/utils/macCa.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Copy-paste from https://github.com/jfromaniello/mac-ca
|
||||||
|
// Babel does not want to transform https://github.com/jfromaniello/mac-ca/blob/fb2b2824c91e3c7f7ffdc8329dd8992e172b967b/lib/formatter.js#L2 because "package" is a reserved word
|
||||||
|
// It is easier to copy the package then add ye another babel transform to change the reserved word on the fly
|
||||||
|
|
||||||
|
import https from 'https';
|
||||||
|
import forge from 'node-forge';
|
||||||
|
|
||||||
|
const validFormats = {
|
||||||
|
der: 0,
|
||||||
|
pem: 1,
|
||||||
|
txt: 2,
|
||||||
|
asn1: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
function myASN(pem: any) {
|
||||||
|
const der = forge.pki.pemToDer(pem);
|
||||||
|
const asn1 = forge.asn1;
|
||||||
|
// @ts-expect-error
|
||||||
|
let crt = asn1.fromDer(der.data.toString('binary')).value[0].value;
|
||||||
|
const serial = crt[0];
|
||||||
|
const hasSerial =
|
||||||
|
serial.tagClass === asn1.Class.CONTEXT_SPECIFIC &&
|
||||||
|
serial.type === 0 &&
|
||||||
|
serial.constructed;
|
||||||
|
crt = crt.slice(hasSerial);
|
||||||
|
return {
|
||||||
|
serial: crt[0],
|
||||||
|
issuer: crt[2],
|
||||||
|
valid: crt[3],
|
||||||
|
subject: crt[4],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function txtFormat(pem: string) {
|
||||||
|
const crt = myASN(pem);
|
||||||
|
const d = new Date();
|
||||||
|
return `Subject\t${crt.subject.value
|
||||||
|
.map((rdn: any) => rdn.value[0].value[1].value)
|
||||||
|
.join('/')}
|
||||||
|
Valid\t${crt.valid.value.map((date: any) => date.value).join(' - ')}
|
||||||
|
Saved\t${d.toLocaleDateString()} ${d
|
||||||
|
.toTimeString()
|
||||||
|
.replace(/\s*\(.*\)\s*/, '')} by mac-ca
|
||||||
|
${pem}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transform = function (format: any) {
|
||||||
|
return function (pem: any) {
|
||||||
|
try {
|
||||||
|
switch (format) {
|
||||||
|
case validFormats.der:
|
||||||
|
return forge.pki.pemToDer(pem);
|
||||||
|
case validFormats.pem:
|
||||||
|
return pem;
|
||||||
|
case validFormats.txt:
|
||||||
|
return txtFormat(pem);
|
||||||
|
case validFormats.asn1:
|
||||||
|
return myASN(pem);
|
||||||
|
default:
|
||||||
|
return forge.pki.certificateFromPem(pem);
|
||||||
|
}
|
||||||
|
} catch (er) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
module.exports.all = () => [];
|
||||||
|
module.exports.each = () => {};
|
||||||
|
} else {
|
||||||
|
const child_process = require('child_process');
|
||||||
|
|
||||||
|
const splitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g;
|
||||||
|
const systemRootCertsPath =
|
||||||
|
'/System/Library/Keychains/SystemRootCertificates.keychain';
|
||||||
|
const args = ['find-certificate', '-a', '-p'];
|
||||||
|
|
||||||
|
// eslint-disable-next-line node/no-sync
|
||||||
|
const allTrusted = child_process
|
||||||
|
.spawnSync('/usr/bin/security', args)
|
||||||
|
.stdout.toString()
|
||||||
|
.split(splitPattern);
|
||||||
|
|
||||||
|
// eslint-disable-next-line node/no-sync
|
||||||
|
const allRoot = child_process
|
||||||
|
.spawnSync('/usr/bin/security', args.concat(systemRootCertsPath))
|
||||||
|
.stdout.toString()
|
||||||
|
.split(splitPattern);
|
||||||
|
|
||||||
|
https.globalAgent.options.ca = https.globalAgent.options.ca || [];
|
||||||
|
|
||||||
|
const ca = https.globalAgent.options.ca;
|
||||||
|
|
||||||
|
function duplicated(cert: any, index: any, arr: any) {
|
||||||
|
return arr.indexOf(cert) === index;
|
||||||
|
}
|
||||||
|
|
||||||
|
const all = allTrusted.concat(allRoot);
|
||||||
|
|
||||||
|
all.filter(duplicated).forEach((cert: any) => (ca as any).push(cert));
|
||||||
|
|
||||||
|
module.exports.der2 = validFormats;
|
||||||
|
|
||||||
|
module.exports.all = function (format: any) {
|
||||||
|
return all.map(transform(format)).filter((c: any) => c);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.each = function (format: any, callback: any) {
|
||||||
|
if (typeof format === 'function') {
|
||||||
|
callback = format;
|
||||||
|
format = undefined;
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
.map(transform(format))
|
||||||
|
.filter((c: any) => c)
|
||||||
|
.forEach(callback);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,19 +8,13 @@
|
|||||||
"bin": "server.js",
|
"bin": "server.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": "https://github.com/facebook/flipper/issues",
|
"bugs": "https://github.com/facebook/flipper/issues",
|
||||||
"dependenciesComment": "mac-ca is required dynamically for darwin, node-fetch is treated special in electron-requires, not sure why",
|
"dependencies": {},
|
||||||
"dependencies": {
|
|
||||||
"exit-hook": "^2.1.1",
|
|
||||||
"http-proxy": "^1.18.1",
|
|
||||||
"mac-ca": "^1.0.6",
|
|
||||||
"node-fetch": "^2.6.7",
|
|
||||||
"ws": "^8.5.0",
|
|
||||||
"xdg-basedir": "^4"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/http-proxy": "^1.17.8",
|
"@types/http-proxy": "^1.17.8",
|
||||||
"@types/node": "^17.0.31",
|
"@types/node": "^17.0.31",
|
||||||
|
"exit-hook": "^2.1.1",
|
||||||
|
"http-proxy": "^1.18.1",
|
||||||
"chalk": "^4",
|
"chalk": "^4",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"flipper-common": "0.0.0",
|
"flipper-common": "0.0.0",
|
||||||
@@ -31,7 +25,9 @@
|
|||||||
"metro": "^0.70.2",
|
"metro": "^0.70.2",
|
||||||
"open": "^8.3.0",
|
"open": "^8.3.0",
|
||||||
"p-filter": "^2.1.0",
|
"p-filter": "^2.1.0",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1",
|
||||||
|
"ws": "^8.5.0",
|
||||||
|
"xdg-basedir": "^4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {},
|
"peerDependencies": {},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -40,15 +40,6 @@ export async function startFlipperServer(
|
|||||||
settingsString: string,
|
settingsString: string,
|
||||||
enableLauncherSettings: boolean,
|
enableLauncherSettings: boolean,
|
||||||
): Promise<FlipperServerImpl> {
|
): Promise<FlipperServerImpl> {
|
||||||
if (os.platform() === 'darwin') {
|
|
||||||
// By default Node.JS has its internal certificate storage and doesn't use
|
|
||||||
// the system store. Because of this, it's impossible to access ondemand / devserver
|
|
||||||
// which are signed using some internal self-issued FB certificates. These certificates
|
|
||||||
// are automatically installed to MacOS system store on FB machines, so here we're using
|
|
||||||
// this "mac-ca" library to load them into Node.JS.
|
|
||||||
electronRequire('mac-ca');
|
|
||||||
}
|
|
||||||
|
|
||||||
const execPath = process.execPath;
|
const execPath = process.execPath;
|
||||||
const appPath = rootDir;
|
const appPath = rootDir;
|
||||||
const isProduction =
|
const isProduction =
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-devtools-installer": "^3.2.0",
|
"electron-devtools-installer": "^3.2.0",
|
||||||
"fix-path": "^3.0.0",
|
"fix-path": "^3.0.0",
|
||||||
"mac-ca": "^1.0.6",
|
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"ws": "^8.5.0",
|
"ws": "^8.5.0",
|
||||||
|
|||||||
@@ -2764,6 +2764,13 @@
|
|||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
form-data "^3.0.0"
|
form-data "^3.0.0"
|
||||||
|
|
||||||
|
"@types/node-forge@^0.10":
|
||||||
|
version "0.10.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.10.10.tgz#07ffccf0f7f3ebb97de67446555912803be50e7b"
|
||||||
|
integrity sha512-iixn5bedlE9fm/5mN7fPpXraXlxCVrnNWHZekys8c5fknridLVWGnNRqlaWpenwaijIuB3bNI0lEOm+JD6hZUA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "16.11.0"
|
version "16.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.0.tgz#4b95f2327bacd1ef8f08d8ceda193039c5d7f52e"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.0.tgz#4b95f2327bacd1ef8f08d8ceda193039c5d7f52e"
|
||||||
@@ -8806,13 +8813,6 @@ lz-string@^1.4.4:
|
|||||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||||
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
|
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
|
||||||
|
|
||||||
mac-ca@^1.0.6:
|
|
||||||
version "1.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/mac-ca/-/mac-ca-1.0.6.tgz#89860edfeebcc4593567044281ab3500961ec15f"
|
|
||||||
integrity sha512-uuCaT+41YtIQlDDvbigP1evK1iUk97zRirP9+8rZJz8x0eIQZG8Z7YQegMTsCiMesLPb6LBgCS95uyAvVA1tmg==
|
|
||||||
dependencies:
|
|
||||||
node-forge "^0.10.0"
|
|
||||||
|
|
||||||
make-dir@^1.0.0:
|
make-dir@^1.0.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||||
|
|||||||
Reference in New Issue
Block a user