/** * 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 process from 'process'; import chalk from 'chalk'; import path from 'path'; import {attachDevServer} from './attachDevServer'; import {initializeLogger} from './logger'; import fs from 'fs-extra'; import yargs from 'yargs'; import open from 'open'; import {initCompanionEnv} from 'flipper-server-companion'; import {startFlipperServer, startServer} from 'flipper-server-core'; import {isTest} from 'flipper-common'; import exitHook from 'exit-hook'; const argv = yargs .usage('yarn flipper-server [args]') .options({ port: { describe: 'TCP port to serve on', type: 'number', default: 52342, }, bundler: { describe: 'Serve the UI bundle from source. This option only works for source checkouts', type: 'boolean', default: false, }, open: { describe: 'Open Flipper in the default browser after starting', type: 'boolean', default: true, }, failFast: { describe: 'Exit the process immediately if the server cannot start, for example due to an incorrect configuration.', type: 'boolean', default: false, }, settingsString: { describe: `override the existing defaults settings of flipper (settings.json file) e.g "{"androidHome":"/usr/local/bin","enableAndroid":true}"`, type: 'string', default: '', }, launcherSettings: { describe: 'Open Flipper with the configuration stored in .config folder for the launcher', type: 'boolean', default: true, }, tcp: { describe: 'Open a TCP port (--no-tcp can be specified as to use unix-domain-socket exclusively)', type: 'boolean', default: true, }, }) .version('DEV') .help() .parse(process.argv.slice(1)); console.log( `Starting flipper server with ${ argv.bundler ? 'UI bundle from source' : 'pre-bundled UI' }`, ); const rootDir = argv.bundler ? path.resolve(__dirname, '..', '..') : path.resolve(__dirname, '..'); // in pre packaged versions of the server, static is copied inside the package const staticDir = path.join(rootDir, 'static'); async function start() { const enhanceLogger = await initializeLogger(staticDir); let keytar: any = undefined; try { if (!isTest()) { const keytarPath = path.join( staticDir, 'native-modules', `keytar-${process.platform}-${process.arch}.node`, ); if (!(await fs.pathExists(keytarPath))) { throw new Error( `Keytar binary does not exist for platform ${process.platform}-${process.arch}`, ); } keytar = electronRequire(keytarPath); } } catch (e) { console.error('Failed to load keytar:', e); } const {app, server, socket, readyForIncomingConnections} = await startServer({ staticDir, entry: `index.web${argv.bundler ? '.dev' : ''}.html`, port: argv.port, tcp: argv.tcp, }); const flipperServer = await startFlipperServer( rootDir, staticDir, argv.settingsString, argv.launcherSettings, keytar, 'external', ); exitHook(async () => { await flipperServer.close(); }); enhanceLogger((logEntry) => { flipperServer.emit('server-log', logEntry); }); const companionEnv = await initCompanionEnv(flipperServer); if (argv.failFast) { flipperServer.on('server-state', ({state}) => { if (state === 'error') { console.error( '[flipper-server-process-exit] state changed to error, process will exit.', ); process.exit(1); } }); } await flipperServer.connect(); if (argv.bundler) { await attachDevServer(app, server, socket, rootDir); } await readyForIncomingConnections(flipperServer, companionEnv); } process.on('uncaughtException', (error) => { console.error( '[flipper-server-process-exit] uncaught exception, process will exit.', error, ); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.warn( '[flipper-server] unhandled rejection for:', promise, 'reason:', reason, ); }); start() .then(() => { if (!argv.tcp) { console.log('Flipper server started and listening'); return; } console.log( 'Flipper server started and listening at port ' + chalk.green(argv.port), ); const url = `http://localhost:${argv.port}`; console.log('Go to: ' + chalk.green(chalk.bold(url))); if (argv.open) { open(url); } }) .catch((e) => { console.error(chalk.red('Server startup error: '), e); process.exit(1); });