mirror of https://github.com/status-im/metro.git
Exposes HMR through the Metro API
Reviewed By: rafeca Differential Revision: D6692912 fbshipit-source-id: 6f119170c40fb99bf2cad83d00edba91bcbbe1c9
This commit is contained in:
parent
cfe3670a07
commit
1cad201448
|
@ -48,7 +48,7 @@ exports.builder = (yargs: Yargs) => {
|
||||||
yargs.option('secure-key', {type: 'string'});
|
yargs.option('secure-key', {type: 'string'});
|
||||||
yargs.option('secure-cert', {type: 'string'});
|
yargs.option('secure-cert', {type: 'string'});
|
||||||
|
|
||||||
yargs.option('legacy-bundler', {type: 'boolean'});
|
yargs.option('hmr-enabled', {alias: 'hmr', type: 'boolean'});
|
||||||
|
|
||||||
yargs.option('config', {alias: 'c', type: 'string'});
|
yargs.option('config', {alias: 'c', type: 'string'});
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,12 @@ const Config = require('./Config');
|
||||||
const Http = require('http');
|
const Http = require('http');
|
||||||
const Https = require('https');
|
const Https = require('https');
|
||||||
const MetroBundler = require('./shared/output/bundle');
|
const MetroBundler = require('./shared/output/bundle');
|
||||||
|
const MetroHmrServer = require('./HmrServer');
|
||||||
const MetroServer = require('./Server');
|
const MetroServer = require('./Server');
|
||||||
const TerminalReporter = require('./lib/TerminalReporter');
|
const TerminalReporter = require('./lib/TerminalReporter');
|
||||||
const TransformCaching = require('./lib/TransformCaching');
|
const TransformCaching = require('./lib/TransformCaching');
|
||||||
|
|
||||||
|
const attachWebsocketServer = require('./lib/attachWebsocketServer');
|
||||||
const defaults = require('./defaults');
|
const defaults = require('./defaults');
|
||||||
|
|
||||||
const {realpath} = require('fs');
|
const {realpath} = require('fs');
|
||||||
|
@ -27,6 +29,7 @@ const {readFile} = require('fs-extra');
|
||||||
const {Terminal} = require('metro-core');
|
const {Terminal} = require('metro-core');
|
||||||
|
|
||||||
import type {ConfigT} from './Config';
|
import type {ConfigT} from './Config';
|
||||||
|
import type {Reporter} from './lib/reporting';
|
||||||
import type {RequestOptions, OutputOptions} from './shared/types.flow.js';
|
import type {RequestOptions, OutputOptions} from './shared/types.flow.js';
|
||||||
import type {Options as ServerOptions} from './shared/types.flow';
|
import type {Options as ServerOptions} from './shared/types.flow';
|
||||||
import type {IncomingMessage, ServerResponse} from 'http';
|
import type {IncomingMessage, ServerResponse} from 'http';
|
||||||
|
@ -40,6 +43,7 @@ type PublicMetroOptions = {|
|
||||||
maxWorkers?: number,
|
maxWorkers?: number,
|
||||||
port?: ?number,
|
port?: ?number,
|
||||||
projectRoots: Array<string>,
|
projectRoots: Array<string>,
|
||||||
|
reporter?: Reporter,
|
||||||
// deprecated
|
// deprecated
|
||||||
resetCache?: boolean,
|
resetCache?: boolean,
|
||||||
|};
|
|};
|
||||||
|
@ -71,10 +75,9 @@ async function runMetro({
|
||||||
// $FlowFixMe TODO t0 https://github.com/facebook/flow/issues/183
|
// $FlowFixMe TODO t0 https://github.com/facebook/flow/issues/183
|
||||||
port = null,
|
port = null,
|
||||||
projectRoots = [],
|
projectRoots = [],
|
||||||
|
reporter = new TerminalReporter(new Terminal(process.stdout)),
|
||||||
watch = false,
|
watch = false,
|
||||||
}: PrivateMetroOptions): Promise<MetroServer> {
|
}: PrivateMetroOptions): Promise<MetroServer> {
|
||||||
const reporter = new TerminalReporter(new Terminal(process.stdout));
|
|
||||||
|
|
||||||
const normalizedConfig = config ? Config.normalize(config) : Config.DEFAULT;
|
const normalizedConfig = config ? Config.normalize(config) : Config.DEFAULT;
|
||||||
|
|
||||||
const assetExts = defaults.assetExts.concat(
|
const assetExts = defaults.assetExts.concat(
|
||||||
|
@ -163,6 +166,7 @@ exports.createConnectMiddleware = async function(
|
||||||
: Config.DEFAULT;
|
: Config.DEFAULT;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
metroServer,
|
||||||
middleware: normalizedConfig.enhanceMiddleware(metroServer.processRequest),
|
middleware: normalizedConfig.enhanceMiddleware(metroServer.processRequest),
|
||||||
end() {
|
end() {
|
||||||
metroServer.end();
|
metroServer.end();
|
||||||
|
@ -178,21 +182,25 @@ type RunServerOptions = {|
|
||||||
secure?: boolean,
|
secure?: boolean,
|
||||||
secureKey?: string,
|
secureKey?: string,
|
||||||
secureCert?: string,
|
secureCert?: string,
|
||||||
|
hmrEnabled?: boolean,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
exports.runServer = async (options: RunServerOptions) => {
|
exports.runServer = async (options: RunServerOptions) => {
|
||||||
const port = options.port || 8080;
|
const port = options.port || 8080;
|
||||||
|
const reporter =
|
||||||
|
options.reporter || new TerminalReporter(new Terminal(process.stdout));
|
||||||
|
|
||||||
// Lazy require
|
// Lazy require
|
||||||
const connect = require('connect');
|
const connect = require('connect');
|
||||||
|
|
||||||
const serverApp = connect();
|
const serverApp = connect();
|
||||||
|
|
||||||
const {middleware, end} = await exports.createConnectMiddleware({
|
const {metroServer, middleware, end} = await exports.createConnectMiddleware({
|
||||||
config: options.config,
|
config: options.config,
|
||||||
maxWorkers: options.maxWorkers,
|
maxWorkers: options.maxWorkers,
|
||||||
port,
|
port,
|
||||||
projectRoots: options.projectRoots,
|
projectRoots: options.projectRoots,
|
||||||
|
reporter,
|
||||||
resetCache: options.resetCache,
|
resetCache: options.resetCache,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -212,6 +220,14 @@ exports.runServer = async (options: RunServerOptions) => {
|
||||||
httpServer = Http.createServer(serverApp);
|
httpServer = Http.createServer(serverApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.hmrEnabled) {
|
||||||
|
attachWebsocketServer({
|
||||||
|
httpServer,
|
||||||
|
path: '/hot',
|
||||||
|
websocketServer: new MetroHmrServer(metroServer, reporter),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
httpServer.listen(port, options.host, () => {
|
httpServer.listen(port, options.host, () => {
|
||||||
options.onReady && options.onReady(httpServer);
|
options.onReady && options.onReady(httpServer);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import type {Server as HttpServer} from 'http';
|
||||||
|
import type {Server as HttpsServer} from 'https';
|
||||||
|
|
||||||
|
type WebsocketServiceInterface<T> = {
|
||||||
|
+onClientConnect: (
|
||||||
|
url: string,
|
||||||
|
sendFn: (data: string) => mixed,
|
||||||
|
) => Promise<T>,
|
||||||
|
+onClientDisconnect?: (client: T) => mixed,
|
||||||
|
+onClientError?: (client: T, e: Error) => mixed,
|
||||||
|
+onClientMessage?: (client: T, message: string) => mixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
type HMROptions<TClient> = {
|
||||||
|
httpServer: HttpServer | HttpsServer,
|
||||||
|
websocketServer: WebsocketServiceInterface<TClient>,
|
||||||
|
path: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a websocket server to an already existing HTTP[S] server, and forward
|
||||||
|
* the received events on the given "websocketServer" parameter. It must be an
|
||||||
|
* object with the following fields:
|
||||||
|
*
|
||||||
|
* - onClientConnect
|
||||||
|
* - onClientError
|
||||||
|
* - onClientMessage
|
||||||
|
* - onClientDisconnect
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function attachWebsocketServer<TClient: Object>({
|
||||||
|
httpServer,
|
||||||
|
websocketServer,
|
||||||
|
path,
|
||||||
|
}: HMROptions<TClient>) {
|
||||||
|
const WebSocketServer = require('ws').Server;
|
||||||
|
const wss = new WebSocketServer({
|
||||||
|
server: httpServer,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
|
wss.on('connection', async ws => {
|
||||||
|
let connected = true;
|
||||||
|
const url = ws.upgradeReq.url;
|
||||||
|
|
||||||
|
const sendFn = (...args) => {
|
||||||
|
if (connected) {
|
||||||
|
ws.send(...args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = await websocketServer.onClientConnect(url, sendFn);
|
||||||
|
|
||||||
|
ws.on('error', e => {
|
||||||
|
websocketServer.onClientError && websocketServer.onClientError(client, e);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('close', () => {
|
||||||
|
websocketServer.onClientDisconnect &&
|
||||||
|
websocketServer.onClientDisconnect(client);
|
||||||
|
connected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('message', message => {
|
||||||
|
websocketServer.onClientMessage &&
|
||||||
|
websocketServer.onClientMessage(client, message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in New Issue