Automatically watches the metro configuration file

Reviewed By: BYK

Differential Revision: D6408358

fbshipit-source-id: d167534c9c51c3c079148d982ef4ab44c8be0d75
This commit is contained in:
Maël Nison 2017-11-28 04:17:21 -08:00 committed by Facebook Github Bot
parent b282031517
commit 5cc0939454
4 changed files with 92 additions and 34 deletions

View File

@ -19,12 +19,20 @@ import type {ConfigT} from './Config';
const METRO_CONFIG_FILENAME = 'metro.config.js'; const METRO_CONFIG_FILENAME = 'metro.config.js';
exports.findMetroConfig = async function( exports.watchFile = async function(
filename: ?string, filename: string,
): Promise<$Shape<ConfigT>> { callback: () => *,
): Promise<void> {
fs.watchFile(filename, () => {
callback();
});
await callback();
};
exports.findMetroConfig = async function(filename: ?string): Promise<?string> {
if (filename) { if (filename) {
// $FlowFixMe: We want this require to be dynamic return path.resolve(process.cwd(), filename);
return require(path.resolve(process.cwd(), filename));
} else { } else {
let previous; let previous;
let current = process.cwd(); let current = process.cwd();
@ -33,14 +41,25 @@ exports.findMetroConfig = async function(
const filename = path.join(current, METRO_CONFIG_FILENAME); const filename = path.join(current, METRO_CONFIG_FILENAME);
if (fs.existsSync(filename)) { if (fs.existsSync(filename)) {
// $FlowFixMe: We want this require to be dynamic return filename;
return require(filename);
} }
previous = current; previous = current;
current = path.dirname(current); current = path.dirname(current);
} while (previous !== current); } while (previous !== current);
return null;
}
};
exports.fetchMetroConfig = async function(
filename: ?string,
): Promise<$Shape<ConfigT>> {
const location = await exports.findMetroConfig(filename);
if (location) {
// $FlowFixMe: We want this require to be dynamic
return require(location);
} else {
return {}; return {};
} }
}; };

View File

@ -16,7 +16,7 @@ const MetroApi = require('..');
const os = require('os'); const os = require('os');
const {findMetroConfig, makeAsyncCommand} = require('../cli-utils'); const {fetchMetroConfig, makeAsyncCommand} = require('../cli-utils');
import typeof Yargs from 'yargs'; import typeof Yargs from 'yargs';
@ -52,6 +52,6 @@ exports.builder = (yargs: Yargs) => {
// eslint-disable-next-line no-unclear-flowtypes // eslint-disable-next-line no-unclear-flowtypes
exports.handler = makeAsyncCommand(async (argv: any) => { exports.handler = makeAsyncCommand(async (argv: any) => {
argv.config = await findMetroConfig(argv.config); const config = await fetchMetroConfig(argv.config);
await MetroApi.runBuild(argv); await MetroApi.runBuild({...argv, config});
}); });

View File

@ -16,7 +16,13 @@ const MetroApi = require('..');
const os = require('os'); const os = require('os');
const {findMetroConfig, makeAsyncCommand} = require('../cli-utils'); const {
findMetroConfig,
fetchMetroConfig,
watchFile,
makeAsyncCommand,
} = require('../cli-utils');
const {promisify} = require('util');
import typeof Yargs from 'yargs'; import typeof Yargs from 'yargs';
@ -49,15 +55,44 @@ exports.builder = (yargs: Yargs) => {
// eslint-disable-next-line no-unclear-flowtypes // eslint-disable-next-line no-unclear-flowtypes
exports.handler = makeAsyncCommand(async (argv: any) => { exports.handler = makeAsyncCommand(async (argv: any) => {
argv.config = await findMetroConfig(argv.config); let server = null;
let restarting = false;
await MetroApi.runServer({ async function restart() {
if (restarting) {
return;
} else {
restarting = true;
}
if (server) {
console.log('Configuration changed... restarting the server...');
await promisify(server.close).call(server);
}
const config = await fetchMetroConfig(argv.config);
server = await MetroApi.runServer({
...argv, ...argv,
onReady(server) { config,
onReady,
});
restarting = false;
}
function onReady(server) {
console.log( console.log(
`The HTTP server is ready to accept requests on ${server.address() `The HTTP server is ready to accept requests on ${server.address()
.address}:${server.address().port}`, .address}:${server.address().port}`,
); );
}, }
});
const metroConfigLocation = await findMetroConfig(argv.config);
if (metroConfigLocation) {
await watchFile(metroConfigLocation, restart);
} else {
await restart();
}
}); });

View File

@ -145,8 +145,13 @@ exports.createConnectMiddleware = async function(
watch: true, watch: true,
}); });
return (req: IncomingMessage, res: ServerResponse) => { return {
middleware(req: IncomingMessage, res: ServerResponse) {
return metroServer.processRequest(req, res); return metroServer.processRequest(req, res);
},
end() {
metroServer.end();
},
}; };
}; };
@ -163,13 +168,13 @@ type RunServerOptions = {|
exports.runServer = async (options: RunServerOptions) => { exports.runServer = async (options: RunServerOptions) => {
const serverApp = connect(); const serverApp = connect();
const metroMiddleware = exports.createConnectMiddleware({ const {middleware, end} = await exports.createConnectMiddleware({
config: options.config, config: options.config,
maxWorkers: options.maxWorkers, maxWorkers: options.maxWorkers,
projectRoots: options.projectRoots, projectRoots: options.projectRoots,
}); });
serverApp.use(metroMiddleware); serverApp.use(middleware);
let httpServer; let httpServer;
@ -185,7 +190,6 @@ exports.runServer = async (options: RunServerOptions) => {
httpServer = Http.createServer(serverApp); httpServer = Http.createServer(serverApp);
} }
// $FlowFixMe: The port parameter IS optional
httpServer.listen(options.port, options.host, () => { httpServer.listen(options.port, options.host, () => {
options.onReady && options.onReady(httpServer); options.onReady && options.onReady(httpServer);
}); });
@ -195,15 +199,15 @@ exports.runServer = async (options: RunServerOptions) => {
// timeout of 120 seconds to respond to a request. // timeout of 120 seconds to respond to a request.
httpServer.timeout = 0; httpServer.timeout = 0;
return new Promise((resolve, reject) => {
httpServer.on('error', error => { httpServer.on('error', error => {
reject(error); end();
}); });
httpServer.on('close', () => { httpServer.on('close', () => {
resolve(); end();
});
}); });
return httpServer;
}; };
type RunBuildOptions = {| type RunBuildOptions = {|