1import { openJsInspector, queryAllInspectorAppsAsync } from '@expo/dev-server'; 2import assert from 'assert'; 3import chalk from 'chalk'; 4 5import * as Log from '../../log'; 6import { learnMore } from '../../utils/link'; 7import { selectAsync } from '../../utils/prompts'; 8import { DevServerManager } from '../server/DevServerManager'; 9import { BLT, printHelp, printItem, printQRCode, printUsage, StartOptions } from './commandsTable'; 10 11/** Wraps the DevServerManager and adds an interface for user actions. */ 12export class DevServerManagerActions { 13 constructor(private devServerManager: DevServerManager) {} 14 15 printDevServerInfo( 16 options: Pick<StartOptions, 'devClient' | 'isWebSocketsEnabled' | 'platforms'> 17 ) { 18 // If native dev server is running, print its URL. 19 if (this.devServerManager.getNativeDevServerPort()) { 20 try { 21 const url = this.devServerManager.getDefaultDevServer().getNativeRuntimeUrl()!; 22 23 printQRCode(url); 24 Log.log(printItem(chalk`Metro waiting on {underline ${url}}`)); 25 // TODO: if development build, change this message! 26 Log.log(printItem('Scan the QR code above with Expo Go (Android) or the Camera app (iOS)')); 27 } catch (error) { 28 // @ts-ignore: If there is no development build scheme, then skip the QR code. 29 if (error.code !== 'NO_DEV_CLIENT_SCHEME') { 30 throw error; 31 } else { 32 const serverUrl = this.devServerManager.getDefaultDevServer().getDevServerUrl(); 33 Log.log(printItem(chalk`Metro waiting on {underline ${serverUrl}}`)); 34 Log.log(printItem(`Linking is disabled because the client scheme cannot be resolved.`)); 35 } 36 } 37 } 38 39 const webUrl = this.devServerManager 40 .getWebDevServer() 41 ?.getDevServerUrl({ hostType: 'localhost' }); 42 if (webUrl) { 43 Log.log(); 44 Log.log(printItem(chalk`Webpack waiting on {underline ${webUrl}}`)); 45 Log.log( 46 chalk.gray(printItem('Expo Webpack (web) is in beta, and subject to breaking changes!')) 47 ); 48 } 49 50 printUsage(options, { verbose: false }); 51 printHelp(); 52 Log.log(); 53 } 54 55 async openJsInspectorAsync() { 56 Log.log('Opening JavaScript inspector in the browser...'); 57 const port = this.devServerManager.getNativeDevServerPort(); 58 assert(port, 'Metro dev server is not running'); 59 const metroServerOrigin = `http://localhost:${port}`; 60 const apps = await queryAllInspectorAppsAsync(metroServerOrigin); 61 if (!apps.length) { 62 Log.warn( 63 `No compatible apps connected. This feature is only available for apps using the Hermes runtime. ${learnMore( 64 'https://docs.expo.dev/guides/using-hermes/' 65 )}` 66 ); 67 return; 68 } 69 for (const app of apps) { 70 openJsInspector(app); 71 } 72 } 73 74 reloadApp() { 75 Log.log(`${BLT} Reloading apps`); 76 // Send reload requests over the dev servers 77 this.devServerManager.broadcastMessage('reload'); 78 } 79 80 async openMoreToolsAsync() { 81 try { 82 // Options match: Chrome > View > Developer 83 const value = await selectAsync(chalk`Dev tools {dim (native only)}`, [ 84 { title: 'Inspect elements', value: 'toggleElementInspector' }, 85 { title: 'Toggle performance monitor', value: 'togglePerformanceMonitor' }, 86 { title: 'Toggle developer menu', value: 'toggleDevMenu' }, 87 { title: 'Reload app', value: 'reload' }, 88 // TODO: Maybe a "View Source" option to open code. 89 // Toggling Remote JS Debugging is pretty rough, so leaving it disabled. 90 // { title: 'Toggle Remote Debugging', value: 'toggleRemoteDebugging' }, 91 ]); 92 this.devServerManager.broadcastMessage('sendDevCommand', { name: value }); 93 } catch (error: any) { 94 Log.debug(error); 95 // do nothing 96 } finally { 97 printHelp(); 98 } 99 } 100 101 toggleDevMenu() { 102 Log.log(`${BLT} Toggling dev menu`); 103 this.devServerManager.broadcastMessage('devMenu'); 104 } 105} 106