Code clean-ups and createServer migration
Summary: Scope of the diff: 1. Middleware `react-native-github/local-cli` and `react-native-internal-cli` uses a very similar set of middlewares (internal cli extends github version), so I decided to move it to a standalone file (middleware manager) in order to remove duplications and increase readability. 2. Types Seems that after Flow upgrade to version 0.68 there were many type issues to resolve, so all of them were auto-mocked. This is fine, but I'd like to see Flow assists me with `Metro.createServer` -> `Metro.runServer` migration. Hence, I decided to resolve flow mocks, related to runServer. 3. `runServer` signature In `react-native-github` repo I cleaned up `runServer` signature by removing `startCallback` and `readyCallback` from the function parameters and moved them to `runServer` instead. 4. Replace `createServer` by `runServer` In `react-native-github` repo, `createServer` has been replaced by `runServer`. __Some of arguments are not mapped__. Note that this diff will partially break argument mapping. This is intentional. @[100000044482482:ives] will fix it with a new config package. Reviewed By: mjesun Differential Revision: D8711717 fbshipit-source-id: a843ab576360ff7242099910d8f25a9cb0a388c0
This commit is contained in:
parent
ee535fafe3
commit
c4a66a89a2
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2013-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @strict
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
const compression = require('compression');
|
||||||
|
const connect = require('connect');
|
||||||
|
const errorhandler = require('errorhandler');
|
||||||
|
const morgan = require('morgan');
|
||||||
|
const path = require('path');
|
||||||
|
const serveStatic = require('serve-static');
|
||||||
|
const WebSocketServer = require('ws').Server;
|
||||||
|
|
||||||
|
const indexPageMiddleware = require('./indexPage');
|
||||||
|
const copyToClipBoardMiddleware = require('./copyToClipBoardMiddleware');
|
||||||
|
const loadRawBodyMiddleware = require('./loadRawBodyMiddleware');
|
||||||
|
const openStackFrameInEditorMiddleware = require('./openStackFrameInEditorMiddleware');
|
||||||
|
const statusPageMiddleware = require('./statusPageMiddleware.js');
|
||||||
|
const systraceProfileMiddleware = require('./systraceProfileMiddleware.js');
|
||||||
|
const getDevToolsMiddleware = require('./getDevToolsMiddleware');
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
+watchFolders: $ReadOnlyArray<string>,
|
||||||
|
+host?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebSocketProxy = {
|
||||||
|
server: WebSocketServer,
|
||||||
|
isChromeConnected: () => boolean,
|
||||||
|
};
|
||||||
|
type Connect = any;
|
||||||
|
|
||||||
|
module.exports = class MiddlewareManager {
|
||||||
|
app: Connect;
|
||||||
|
options: Options;
|
||||||
|
|
||||||
|
constructor(options: Options) {
|
||||||
|
const debuggerUIFolder = path.join(__dirname, 'util', 'debugger-ui');
|
||||||
|
|
||||||
|
this.options = options;
|
||||||
|
this.app = connect()
|
||||||
|
.use(loadRawBodyMiddleware)
|
||||||
|
.use(compression())
|
||||||
|
.use('/debugger-ui', serveStatic(debuggerUIFolder))
|
||||||
|
.use(openStackFrameInEditorMiddleware(this.options))
|
||||||
|
.use(copyToClipBoardMiddleware)
|
||||||
|
.use(statusPageMiddleware)
|
||||||
|
.use(systraceProfileMiddleware)
|
||||||
|
.use(indexPageMiddleware)
|
||||||
|
.use(morgan('combined'))
|
||||||
|
.use(errorhandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
serveStatic = (folder: string) => {
|
||||||
|
this.app.use(serveStatic(folder));
|
||||||
|
};
|
||||||
|
|
||||||
|
getConnectInstance = () => this.app;
|
||||||
|
|
||||||
|
attachDevToolsSocket = (socket: WebSocketProxy) => {
|
||||||
|
this.app.use(
|
||||||
|
getDevToolsMiddleware(this.options, () => socket.isChromeConnected()),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
|
@ -27,8 +27,8 @@ function launchDevTools({host, watchFolders}, isChromeConnected) {
|
||||||
// Explicit config always wins
|
// Explicit config always wins
|
||||||
var customDebugger = process.env.REACT_DEBUGGER;
|
var customDebugger = process.env.REACT_DEBUGGER;
|
||||||
if (customDebugger) {
|
if (customDebugger) {
|
||||||
var projects = watchFolders.map(escapePath).join(' ');
|
var folders = watchFolders.map(escapePath).join(' ');
|
||||||
var command = customDebugger + ' ' + projects;
|
var command = customDebugger + ' ' + folders;
|
||||||
console.log('Starting custom debugger by executing: ' + command);
|
console.log('Starting custom debugger by executing: ' + command);
|
||||||
exec(command, function(error, stdout, stderr) {
|
exec(command, function(error, stdout, stderr) {
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
|
|
|
@ -14,132 +14,69 @@ require('../../setupBabel')();
|
||||||
|
|
||||||
const Metro = require('metro');
|
const Metro = require('metro');
|
||||||
|
|
||||||
const HmrServer = require('metro/src/HmrServer');
|
|
||||||
|
|
||||||
const {Terminal} = require('metro-core');
|
const {Terminal} = require('metro-core');
|
||||||
|
|
||||||
const attachWebsocketServer = require('metro/src/lib/attachWebsocketServer');
|
|
||||||
const compression = require('compression');
|
|
||||||
const connect = require('connect');
|
|
||||||
const copyToClipBoardMiddleware = require('./middleware/copyToClipBoardMiddleware');
|
|
||||||
const defaultAssetExts = Metro.defaults.assetExts;
|
|
||||||
const defaultSourceExts = Metro.defaults.sourceExts;
|
|
||||||
const defaultPlatforms = Metro.defaults.platforms;
|
|
||||||
const defaultProvidesModuleNodeModules =
|
|
||||||
Metro.defaults.providesModuleNodeModules;
|
|
||||||
const errorhandler = require('errorhandler');
|
|
||||||
const fs = require('fs');
|
|
||||||
const getDevToolsMiddleware = require('./middleware/getDevToolsMiddleware');
|
|
||||||
const http = require('http');
|
|
||||||
const https = require('https');
|
|
||||||
const indexPageMiddleware = require('./middleware/indexPage');
|
|
||||||
const loadRawBodyMiddleware = require('./middleware/loadRawBodyMiddleware');
|
|
||||||
const messageSocket = require('./util/messageSocket.js');
|
|
||||||
const morgan = require('morgan');
|
|
||||||
const openStackFrameInEditorMiddleware = require('./middleware/openStackFrameInEditorMiddleware');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const serveStatic = require('serve-static');
|
const MiddlewareManager = require('./middleware/MiddlewareManager');
|
||||||
const statusPageMiddleware = require('./middleware/statusPageMiddleware.js');
|
|
||||||
const systraceProfileMiddleware = require('./middleware/systraceProfileMiddleware.js');
|
|
||||||
const webSocketProxy = require('./util/webSocketProxy.js');
|
|
||||||
|
|
||||||
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
|
|
||||||
|
|
||||||
import type {ConfigT} from 'metro';
|
import type {ConfigT} from 'metro';
|
||||||
/* $FlowFixMe(site=react_native_oss) */
|
|
||||||
import type {Reporter} from 'metro/src/lib/reporting';
|
|
||||||
|
|
||||||
export type Args = {|
|
export type Args = {|
|
||||||
+assetExts: $ReadOnlyArray<string>,
|
+assetExts: $ReadOnlyArray<string>,
|
||||||
|
+cert: string,
|
||||||
|
+customLogReporterPath?: string,
|
||||||
+host: string,
|
+host: string,
|
||||||
|
+https: boolean,
|
||||||
+maxWorkers: number,
|
+maxWorkers: number,
|
||||||
|
+key: string,
|
||||||
+nonPersistent: boolean,
|
+nonPersistent: boolean,
|
||||||
+platforms: $ReadOnlyArray<string>,
|
+platforms: $ReadOnlyArray<string>,
|
||||||
+port: number,
|
+port: number,
|
||||||
+projectRoot: string,
|
+projectRoot: string,
|
||||||
|
+providesModuleNodeModules: Array<string>,
|
||||||
+resetCache: boolean,
|
+resetCache: boolean,
|
||||||
+sourceExts: $ReadOnlyArray<string>,
|
+sourceExts: $ReadOnlyArray<string>,
|
||||||
|
+transformer?: string,
|
||||||
+verbose: boolean,
|
+verbose: boolean,
|
||||||
+watchFolders: $ReadOnlyArray<string>,
|
+watchFolders: $ReadOnlyArray<string>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
function runServer(
|
async function runServer(args: Args, config: ConfigT) {
|
||||||
args: Args,
|
|
||||||
config: ConfigT,
|
|
||||||
// FIXME: this is weird design. The top-level should pass down a custom
|
|
||||||
// reporter rather than passing it up as argument to an event.
|
|
||||||
startedCallback: (reporter: Reporter) => mixed,
|
|
||||||
readyCallback: (reporter: Reporter) => mixed,
|
|
||||||
) {
|
|
||||||
var wsProxy = null;
|
|
||||||
var ms = null;
|
|
||||||
|
|
||||||
const terminal = new Terminal(process.stdout);
|
const terminal = new Terminal(process.stdout);
|
||||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
|
||||||
* found when Flow v0.68 was deployed. To see the error delete this comment
|
|
||||||
* and run Flow. */
|
|
||||||
const ReporterImpl = getReporterImpl(args.customLogReporterPath || null);
|
const ReporterImpl = getReporterImpl(args.customLogReporterPath || null);
|
||||||
const reporter = new ReporterImpl(terminal);
|
const reporter = new ReporterImpl(terminal);
|
||||||
const packagerServer = getPackagerServer(args, config, reporter);
|
const middlewareManager = new MiddlewareManager(args);
|
||||||
startedCallback(reporter);
|
|
||||||
|
|
||||||
const app = connect()
|
args.watchFolders.forEach(middlewareManager.serveStatic);
|
||||||
.use(loadRawBodyMiddleware)
|
|
||||||
.use(compression())
|
|
||||||
.use(
|
|
||||||
'/debugger-ui',
|
|
||||||
serveStatic(path.join(__dirname, 'util', 'debugger-ui')),
|
|
||||||
)
|
|
||||||
.use(
|
|
||||||
getDevToolsMiddleware(args, () => wsProxy && wsProxy.isChromeConnected()),
|
|
||||||
)
|
|
||||||
.use(getDevToolsMiddleware(args, () => ms && ms.isChromeConnected()))
|
|
||||||
.use(openStackFrameInEditorMiddleware(args))
|
|
||||||
.use(copyToClipBoardMiddleware)
|
|
||||||
.use(statusPageMiddleware)
|
|
||||||
.use(systraceProfileMiddleware)
|
|
||||||
.use(indexPageMiddleware)
|
|
||||||
.use(packagerServer.processRequest.bind(packagerServer));
|
|
||||||
|
|
||||||
args.watchFolders.forEach(root => app.use(serveStatic(root)));
|
const serverInstance = await Metro.runServer({
|
||||||
|
config: {
|
||||||
app.use(morgan('combined')).use(errorhandler());
|
...config,
|
||||||
|
hmrEnabled: true,
|
||||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
maxWorkers: args.maxWorkers,
|
||||||
* found when Flow v0.68 was deployed. To see the error delete this comment
|
reporter,
|
||||||
* and run Flow. */
|
secure: args.https,
|
||||||
if (args.https && (!args.key || !args.cert)) {
|
secureKey: args.key,
|
||||||
throw new Error('Cannot use https without specifying key and cert options');
|
secureCert: args.cert,
|
||||||
}
|
transformModulePath: args.transformer
|
||||||
|
? path.resolve(args.transformer)
|
||||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
: config.getTransformModulePath(),
|
||||||
* found when Flow v0.68 was deployed. To see the error delete this comment
|
watch: !args.nonPersistent,
|
||||||
* and run Flow. */
|
},
|
||||||
const serverInstance = args.https
|
|
||||||
? https.createServer(
|
|
||||||
{
|
|
||||||
key: fs.readFileSync(args.key),
|
|
||||||
cert: fs.readFileSync(args.cert),
|
|
||||||
},
|
|
||||||
app,
|
|
||||||
)
|
|
||||||
: http.createServer(app);
|
|
||||||
|
|
||||||
serverInstance.listen(args.port, args.host, 511, function() {
|
|
||||||
attachWebsocketServer({
|
|
||||||
httpServer: serverInstance,
|
|
||||||
path: '/hot',
|
|
||||||
websocketServer: new HmrServer(packagerServer),
|
|
||||||
});
|
|
||||||
|
|
||||||
wsProxy = webSocketProxy.attachToServer(serverInstance, '/debugger-proxy');
|
|
||||||
ms = messageSocket.attachToServer(serverInstance, '/message');
|
|
||||||
readyCallback(reporter);
|
|
||||||
});
|
});
|
||||||
// Disable any kind of automatic timeout behavior for incoming
|
|
||||||
// requests in case it takes the packager more than the default
|
// In Node 8, the default keep-alive for an HTTP connection is 5 seconds. In
|
||||||
// timeout of 120 seconds to respond to a request.
|
// early versions of Node 8, this was implemented in a buggy way which caused
|
||||||
serverInstance.timeout = 0;
|
// some HTTP responses (like those containing large JS bundles) to be
|
||||||
|
// terminated early.
|
||||||
|
//
|
||||||
|
// As a workaround, arbitrarily increase the keep-alive from 5 to 30 seconds,
|
||||||
|
// which should be enough to send even the largest of JS bundles.
|
||||||
|
//
|
||||||
|
// For more info: https://github.com/nodejs/node/issues/13391
|
||||||
|
//
|
||||||
|
// $FlowFixMe
|
||||||
|
serverInstance.keepAliveTimeout = 30000;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReporterImpl(customLogReporterPath: ?string) {
|
function getReporterImpl(customLogReporterPath: ?string) {
|
||||||
|
@ -162,53 +99,4 @@ function getReporterImpl(customLogReporterPath: ?string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPackagerServer(args, config, reporter) {
|
|
||||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
|
||||||
* found when Flow v0.68 was deployed. To see the error delete this comment
|
|
||||||
* and run Flow. */
|
|
||||||
const transformModulePath = args.transformer
|
|
||||||
? path.resolve(args.transformer)
|
|
||||||
: config.getTransformModulePath();
|
|
||||||
|
|
||||||
const providesModuleNodeModules =
|
|
||||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
|
|
||||||
* error found when Flow v0.68 was deployed. To see the error delete this
|
|
||||||
* comment and run Flow. */
|
|
||||||
args.providesModuleNodeModules || defaultProvidesModuleNodeModules;
|
|
||||||
|
|
||||||
return Metro.createServer({
|
|
||||||
asyncRequireModulePath: config.getAsyncRequireModulePath(),
|
|
||||||
assetExts: defaultAssetExts.concat(args.assetExts),
|
|
||||||
assetRegistryPath: ASSET_REGISTRY_PATH,
|
|
||||||
blacklistRE: config.getBlacklistRE(),
|
|
||||||
cacheStores: config.cacheStores,
|
|
||||||
cacheVersion: '3',
|
|
||||||
enableBabelRCLookup: config.getEnableBabelRCLookup(),
|
|
||||||
extraNodeModules: config.extraNodeModules,
|
|
||||||
dynamicDepsInPackages: config.dynamicDepsInPackages,
|
|
||||||
getModulesRunBeforeMainModule: config.getModulesRunBeforeMainModule,
|
|
||||||
getPolyfills: config.getPolyfills,
|
|
||||||
getResolverMainFields: config.getResolverMainFields,
|
|
||||||
getRunModuleStatement: config.getRunModuleStatement,
|
|
||||||
getTransformOptions: config.getTransformOptions,
|
|
||||||
hasteImplModulePath: config.hasteImplModulePath,
|
|
||||||
maxWorkers: args.maxWorkers,
|
|
||||||
platforms: defaultPlatforms.concat(args.platforms),
|
|
||||||
polyfillModuleNames: config.getPolyfillModuleNames(),
|
|
||||||
postMinifyProcess: config.postMinifyProcess,
|
|
||||||
postProcessBundleSourcemap: config.postProcessBundleSourcemap,
|
|
||||||
projectRoot: args.projectRoot,
|
|
||||||
providesModuleNodeModules: providesModuleNodeModules,
|
|
||||||
reporter,
|
|
||||||
resetCache: args.resetCache,
|
|
||||||
resolveRequest: config.resolveRequest,
|
|
||||||
sourceExts: args.sourceExts.concat(defaultSourceExts),
|
|
||||||
transformModulePath: transformModulePath,
|
|
||||||
verbose: args.verbose,
|
|
||||||
watch: !args.nonPersistent,
|
|
||||||
watchFolders: args.watchFolders,
|
|
||||||
workerPath: config.getWorkerPath(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = runServer;
|
module.exports = runServer;
|
||||||
|
|
|
@ -19,34 +19,10 @@ import type {Args as RunServerArgs} from './runServer';
|
||||||
/**
|
/**
|
||||||
* Starts the React Native Packager Server.
|
* Starts the React Native Packager Server.
|
||||||
*/
|
*/
|
||||||
function server(argv: mixed, config: RNConfig, args: Object) {
|
function server(argv: mixed, config: RNConfig, args: RunServerArgs) {
|
||||||
const startedCallback = logReporter => {
|
|
||||||
logReporter.update({
|
|
||||||
type: 'initialize_started',
|
|
||||||
port: args.port,
|
|
||||||
projectRoots: args.watchFolders,
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('uncaughtException', error => {
|
|
||||||
logReporter.update({
|
|
||||||
type: 'initialize_failed',
|
|
||||||
port: args.port,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
process.exit(11);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const readyCallback = logReporter => {
|
|
||||||
logReporter.update({
|
|
||||||
type: 'initialize_done',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const runServerArgs: RunServerArgs = args;
|
|
||||||
/* $FlowFixMe(site=react_native_fb) ConfigT shouldn't be extendable. */
|
/* $FlowFixMe(site=react_native_fb) ConfigT shouldn't be extendable. */
|
||||||
const configT: ConfigT = config;
|
const configT: ConfigT = config;
|
||||||
runServer(runServerArgs, configT, startedCallback, readyCallback);
|
runServer(args, configT);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
Loading…
Reference in New Issue