import type CliServerApi from "@react-native-community/cli-server-api" ; import type { Config as CLIConfig } from "@react-native-community/cli-types" ; import { createTerminal , loadMetroConfig , startServer , } from "@rnx-kit/metro-service" ; import chalk from "chalk" ; import type { ReportableEvent , Reporter } from "metro" ; import type { Middleware } from "metro-config" ; import type Server from "metro/src/Server" ; import os from "os" ; import path from "path" ; import qrcode from "qrcode" ; import readline from "readline" ; import { customizeMetroConfig } from "./metro-config" ; import { getKitServerConfig } from "./serve/kit-config" ; import type { TypeScriptValidationOptions } from "./types" ; export type CLIStartOptions = { host : string ; port : number ; projectRoot ? : string ; watchFolders ? : string [ ] ; assetPlugins ? : string [ ] ; sourceExts ? : string [ ] ; maxWorkers ? : number ; customLogReporterPath ? : string ; https ? : boolean ; key ? : string ; cert ? : string ; resetCache ? : boolean ; config ? : string ; interactive : boolean ; } ; function friendlyRequire < T > ( module ) { try { return require ( module ) as T ; } catch ( _ ) { throw new Error ( `Cannot find module '${ module }'. This probably means that '@rnx-kit/cli' is not compatible with the version of '@react-native-community/cli' that you are currently using. Please update to the latest version and try again. If the issue still persists after the update, please file a bug at https://github.com/microsoft/rnx-kit/issues.` ) ; } } export async function rnxStart ( _argv , cliConfig , cliOptions ) { const serverConfig = getKitServerConfig ( cliOptions ) ; if ( ! serverConfig ) { return Promise . resolve ( ) ; } const { createDevServerMiddleware , indexPageMiddleware } = friendlyRequire < typeof CliServerApi > ( "@react-native-community/cli-server-api" ) ; let interactive = cliOptions . interactive ; if ( interactive ) { interactive = process . stdin . isTTY ; if ( ! interactive ) { console . warn ( chalk . yellow ( "Warning: Interactive mode is not supported on this terminal" ) ) ; } } const { terminal , reporter : terminalReporter } = createTerminal ( cliOptions . customLogReporterPath ) ; let reportEventDelegate = undefined ; const reporter = { update ( event ) { terminalReporter . update ( event ) ; if ( reportEventDelegate ) { reportEventDelegate ( event ) ; } if ( interactive && event . type === "dep_graph_loading" ) { const dim = chalk . dim ; const press = dim ( " › Press " ) ; [ [ "r" , "reload the app" ] , [ "d" , "open developer menu" ] , [ "a" , "show bundler address QR code" ] , [ "ctrl-c" , "quit" ] , ] . forEach ( ( [ key , description ] ) => { terminal . log ( press + key + dim ( ` to ${ description }.` ) ) ; } ) ; } } } ; const metroConfig = await loadMetroConfig ( cliConfig , { ... cliOptions , ... ( serverConfig . projectRoot ? { projectRoot : path . resolve ( serverConfig . projectRoot ) } : undefined ) , reporter , ... ( serverConfig . sourceExts ? { sourceExts : serverConfig . sourceExts } : undefined ) , ... ( serverConfig . assetPlugins ? { assetPlugins : serverConfig . assetPlugins . map ( ( p ) => require . resolve ( p ) ) } : undefined ) } ) ; const typescriptValidationOptions = { print : ( message ) : void => { terminal . log ( message ) ; } } ; customizeMetroConfig ( metroConfig , serverConfig . detectCyclicDependencies , serverConfig . detectDuplicateDependencies , serverConfig . typescriptValidation ? typescriptValidationOptions : false , serverConfig . experimental_treeShake ) ; const { middleware , attachToServer } = createDevServerMiddleware ( { host : cliOptions . host , port : metroConfig . server . port , watchFolders : metroConfig . watchFolders } ) ; middleware . use ( indexPageMiddleware ) ; const enhanceMiddleware = metroConfig . server . enhanceMiddleware ; metroConfig . server . enhanceMiddleware = ( metroMiddleware , metroServer ) => { return middleware . use ( enhanceMiddleware ? enhanceMiddleware ( metroMiddleware , metroServer ) : metroMiddleware ) ; } ; const serverInstance = await startServer ( metroConfig , { host : cliOptions . host , secure : cliOptions . https , secureCert : cliOptions . cert , secureKey : cliOptions . key } ) ; const { messageSocket , eventsSocket } = attachToServer ( serverInstance ) ; reportEventDelegate = eventsSocket . reportEvent ; serverInstance . keepAliveTimeout = 30000 ; if ( interactive ) { readline . emitKeypressEvents ( process . stdin ) ; process . stdin . setRawMode ( true ) ; process . stdin . on ( "keypress" , ( _key , data ) => { const { ctrl , name } = data ; if ( ctrl === true ) { switch ( name ) { case "c" : terminal . log ( chalk . green ( "Exiting..." ) ) ; process . exit ( ) ; break ; case "z" : process . emit ( "SIGTSTP" , "SIGTSTP" ) ; break ; } } else { switch ( name ) { case "a" : { const protocol = cliOptions . https ? "https" : "http" ; const host = cliOptions . host || os . hostname ( ) ; const port = metroConfig . server . port ; const url = `${ protocol }://${ host }:${ port }/index.bundle` ; qrcode . toString ( url , { type : "terminal" } , ( _err , qr ) => { terminal . log ( "" ) ; terminal . log ( url + ":" ) ; terminal . log ( qr ) ; } ) ; break ; } case "d" : terminal . log ( chalk . green ( "Opening developer menu..." ) ) ; messageSocket . broadcast ( "devMenu" , undefined ) ; break ; case "r" : terminal . log ( chalk . green ( "Reloading app..." ) ) ; messageSocket . broadcast ( "reload" , undefined ) ; break ; } } } ) ; } }