import { ExpoConfig , getConfig } from '@expo/config' ; import { closeJsInspector , MessageSocket } from '@expo/dev-server' ; import { Server } from 'http' ; import { Analytics , Android , assertValidProjectRoot , Config , ConnectionStatus , DevSession , Env , ProjectSettings , ProjectUtils , startDevServerAsync , StartDevServerOptions , startExpoServerAsync , startReactNativeServerAsync , startTunnelsAsync , stopExpoServerAsync , stopReactNativeServerAsync , stopTunnelsAsync , Webpack , } from '../internal' ; import { watchBabelConfigForProject } from './watchBabelConfig' ; let serverInstance = null ; let messageSocket = null ; param method param params export function broadcastMessage ( method , params ? ) { if ( messageSocket ) { messageSocket . broadcast ( method , params ) ; } } export async function startAsync ( projectRoot , { exp = getConfig ( projectRoot , { skipSDKVersionRequirement : true } ) . exp , ... options } = { } , verbose = true ) { assertValidProjectRoot ( projectRoot ) ; Analytics . logEvent ( 'Start Project' , { developerTool : Config . developerTool , sdkVersion : exp . sdkVersion ?? null } ) ; watchBabelConfigForProject ( projectRoot ) ; if ( options . webOnly ) { await Webpack . startAsync ( projectRoot , { ... options , port : options . webpackPort } ) ; } else if ( Env . shouldUseDevServer ( exp ) || options . devClient ) { [ serverInstance , , messageSocket ] = await startDevServerAsync ( projectRoot , options ) ; } else { await startExpoServerAsync ( projectRoot ) ; await startReactNativeServerAsync ( { projectRoot , exp , options , verbose } ) ; } const { hostType } = await ProjectSettings . readAsync ( projectRoot ) ; if ( ! ConnectionStatus . isOffline ( ) && hostType === 'tunnel' ) { try { await startTunnelsAsync ( projectRoot ) ; } catch ( e ) { ProjectUtils . logError ( projectRoot , 'expo' , `Error starting ngrok: ${ e . message }` ) ; } } const target = ! options . webOnly || Webpack . isTargetingNative ( ) ? 'native' : 'web' ; DevSession . startSession ( projectRoot , exp , target ) ; return exp ; } async function stopDevServerAsync ( ) { return new Promise < void > ( ( resolve , reject ) => { if ( serverInstance ) { closeJsInspector ( ) ; serverInstance . close ( error => { if ( error ) { reject ( error ) ; } else { resolve ( ) ; } } ) ; } } ) ; } async function stopInternalAsync ( projectRoot ) { DevSession . stopSession ( ) ; await Promise . all ( [ Webpack . stopAsync ( projectRoot ) , stopDevServerAsync ( ) , stopExpoServerAsync ( projectRoot ) , stopReactNativeServerAsync ( projectRoot ) , async ( ) => { if ( ! ConnectionStatus . isOffline ( ) ) { try { await stopTunnelsAsync ( projectRoot ) ; } catch ( e ) { ProjectUtils . logError ( projectRoot , 'expo' , `Error stopping ngrok: ${ e . message }` ) ; } } } , await Android . maybeStopAdbDaemonAsync ( ) , ] ) ; } async function forceQuitAsync ( projectRoot ) { const { packagerPid , ngrokPid } = await ProjectSettings . readPackagerInfoAsync ( projectRoot ) ; if ( packagerPid ) { try { process . kill ( packagerPid ) ; } catch ( e ) { } } if ( ngrokPid ) { try { process . kill ( ngrokPid ) ; } catch ( e ) { } } await ProjectSettings . setPackagerInfoAsync ( projectRoot , { expoServerPort : null , packagerPort : null , packagerPid : null , expoServerNgrokUrl : null , packagerNgrokUrl : null , ngrokPid : null , webpackServerPort : null } ) ; } export async function stopAsync ( projectRoot ) { try { const result = await Promise . race ( [ stopInternalAsync ( projectRoot ) , new Promise ( resolve => setTimeout ( resolve , 2000 , 'stopFailed' ) ) , ] ) ; if ( result === 'stopFailed' ) { await forceQuitAsync ( projectRoot ) ; } } catch ( error ) { await forceQuitAsync ( projectRoot ) ; throw error ; } }