import * as PackageManager from '@expo/package-manager' ; import chalk from 'chalk' ; import getenv from 'getenv' ; import requireg from 'requireg' ; import resolveFrom from 'resolve-from' ; import semver from 'semver' ; import { delayAsync , Logger , Prompts } from '../internal' ; const NGROK_REQUIRED_VERSION = '^4.1.0' ; const EXPO_DEBUG = getenv . boolish ( 'EXPO_DEBUG' , false ) ; let _ngrokInstance = null ; export interface NgrokOptions { authtoken ? : string ; port ? : string | number | null ; host ? : string ; httpauth ? : string ; region ? : string ; configPath ? : string ; proto ? : 'http' | 'tcp' | 'tls' ; addr ? : string ; inspect ? : boolean ; auth ? : string ; host_header ? : string ; bind_tls ? : true | false | 'both' ; subdomain ? : string ; hostname ? : string ; crt ? : string ; key ? : string ; client_cas ? : string ; remote_addr ? : string ; } export async function resolveNgrokAsync ( projectRoot , { shouldPrompt = true , autoInstall = false , } = { } ) { const ngrok = await findNgrokBinAsync ( projectRoot ) ; if ( ! ngrok ) { const packageName = `@expo/ngrok@${ NGROK_REQUIRED_VERSION }` ; if ( shouldPrompt ) { if ( ! autoInstall ) { await delayAsync ( 100 ) ; } const answer = autoInstall || ( await Prompts . confirmAsync ( { message : `The package ${ packageName } is required to use tunnels, would you like to install it globally?` , initial : true } ) ) ; if ( answer ) { Logger . global . info ( `Installing ${ packageName } for ${ chalk . bold `tunnel` } support...` ) ; const packageManager = PackageManager . createForProject ( projectRoot , { silent : ! EXPO_DEBUG } ) ; try { await packageManager . addGlobalAsync ( packageName ) ; Logger . global . info ( `Successfully installed ${ packageName }` ) ; } catch ( e ) { e . message = `Failed to install ${ packageName } globally: ${ e . message }` ; throw e ; } return await resolveNgrokAsync ( projectRoot , { shouldPrompt : false } ) ; } } throw new Error ( `Please install ${ packageName } and try again, or try using another hosting method like lan or localhost` ) ; } return ngrok ; } async function resolvePackageFromProjectAsync ( projectRoot ) { try { const ngrokPackagePath = resolveFrom ( projectRoot , '@expo/ngrok/package.json' ) ; const pkg = require ( ngrokPackagePath ) ; if ( pkg && semver . satisfies ( pkg . version , NGROK_REQUIRED_VERSION ) ) { const ngrokPath = resolveFrom ( projectRoot , '@expo/ngrok' ) ; Logger . global . debug ( `Resolving @expo/ngrok from project: "${ ngrokPath }"` ) ; return require ( ngrokPath ) ; } } catch { } return null ; } async function resolveGlobalPackageAsync ( ) { try { const pkg = requireg ( '@expo/ngrok/package.json' , true ) ; if ( semver . satisfies ( pkg . version , NGROK_REQUIRED_VERSION ) ) { Logger . global . debug ( `Resolving global @expo/ngrok from: "${ requireg . resolve ( '@expo/ngrok' ) }"` ) ; return requireg ( '@expo/ngrok' , true ) ; } } catch { } return null ; } async function findNgrokBinAsync ( projectRoot ) { if ( _ngrokInstance ) { return _ngrokInstance ; } const localInstance = await resolvePackageFromProjectAsync ( projectRoot ) ; if ( localInstance ) { _ngrokInstance = localInstance ; return _ngrokInstance ; } const globalInstance = await resolveGlobalPackageAsync ( ) ; if ( globalInstance ) { _ngrokInstance = globalInstance ; return _ngrokInstance ; } return null ; }