18d307f52SEvan Baconimport { ExpoConfig } from '@expo/config-types'; 28d307f52SEvan Baconimport chalk from 'chalk'; 38d307f52SEvan Baconimport qrcode from 'qrcode-terminal'; 48d307f52SEvan Baconimport wrapAnsi from 'wrap-ansi'; 58d307f52SEvan Bacon 68d307f52SEvan Baconimport * as Log from '../../log'; 78d307f52SEvan Bacon 88d307f52SEvan Baconexport const BLT = '\u203A'; 98d307f52SEvan Bacon 108d307f52SEvan Baconexport type StartOptions = { 118d307f52SEvan Bacon isWebSocketsEnabled?: boolean; 128d307f52SEvan Bacon devClient?: boolean; 138d307f52SEvan Bacon reset?: boolean; 148d307f52SEvan Bacon nonPersistent?: boolean; 158d307f52SEvan Bacon maxWorkers?: number; 168d307f52SEvan Bacon platforms?: ExpoConfig['platforms']; 178d307f52SEvan Bacon}; 188d307f52SEvan Bacon 198d307f52SEvan Baconexport const printHelp = (): void => { 208d307f52SEvan Bacon logCommandsTable([{ key: '?', msg: 'show all commands' }]); 218d307f52SEvan Bacon}; 228d307f52SEvan Bacon 238d307f52SEvan Bacon/** Print the world famous 'Expo QR Code'. */ 248d307f52SEvan Baconexport function printQRCode(url: string) { 258d307f52SEvan Bacon qrcode.generate(url, { small: true }, (code) => Log.log(code)); 268d307f52SEvan Bacon} 278d307f52SEvan Bacon 288d307f52SEvan Baconexport const getTerminalColumns = () => process.stdout.columns || 80; 298d307f52SEvan Baconexport const printItem = (text: string): string => 308d307f52SEvan Bacon `${BLT} ` + wrapAnsi(text, getTerminalColumns()).trimStart(); 318d307f52SEvan Bacon 328d307f52SEvan Baconexport function printUsage( 338d307f52SEvan Bacon options: Pick<StartOptions, 'devClient' | 'isWebSocketsEnabled' | 'platforms'>, 348d307f52SEvan Bacon { verbose }: { verbose: boolean } 358d307f52SEvan Bacon) { 368d307f52SEvan Bacon const isMac = process.platform === 'darwin'; 378d307f52SEvan Bacon 388d307f52SEvan Bacon const { platforms = ['ios', 'android', 'web'] } = options; 398d307f52SEvan Bacon 408d307f52SEvan Bacon const isAndroidDisabled = !platforms.includes('android'); 418d307f52SEvan Bacon const isIosDisabled = !platforms.includes('ios'); 428d307f52SEvan Bacon const isWebDisable = !platforms.includes('web'); 438d307f52SEvan Bacon 44a7e47f4dSEvan Bacon const switchMsg = `switch to ${options.devClient === false ? 'development build' : 'Expo Go'}`; 45a7e47f4dSEvan Bacon const target = options.devClient === false ? `Expo Go` : 'development build'; 46a7e47f4dSEvan Bacon 47a7e47f4dSEvan Bacon Log.log(); 48a7e47f4dSEvan Bacon Log.log(printItem(chalk`Using {cyan ${target}}`)); 49a7e47f4dSEvan Bacon 508d307f52SEvan Bacon if (verbose) { 518d307f52SEvan Bacon logCommandsTable([ 52a7e47f4dSEvan Bacon { key: 's', msg: switchMsg }, 538d307f52SEvan Bacon {}, 548d307f52SEvan Bacon { key: 'a', msg: 'open Android', disabled: isAndroidDisabled }, 558d307f52SEvan Bacon { key: 'shift+a', msg: 'select a device or emulator', disabled: isAndroidDisabled }, 568d307f52SEvan Bacon isMac && { key: 'i', msg: 'open iOS simulator', disabled: isIosDisabled }, 578d307f52SEvan Bacon isMac && { key: 'shift+i', msg: 'select a simulator', disabled: isIosDisabled }, 588d307f52SEvan Bacon { key: 'w', msg: 'open web', disabled: isWebDisable }, 598d307f52SEvan Bacon {}, 608d307f52SEvan Bacon { key: 'r', msg: 'reload app' }, 6148103a3dSEvan Bacon !!options.isWebSocketsEnabled && { key: 'j', msg: 'open debugger' }, 628d307f52SEvan Bacon !!options.isWebSocketsEnabled && { key: 'm', msg: 'toggle menu' }, 638d307f52SEvan Bacon !!options.isWebSocketsEnabled && { key: 'shift+m', msg: 'more tools' }, 648d307f52SEvan Bacon { key: 'o', msg: 'open project code in your editor' }, 658d307f52SEvan Bacon { key: 'c', msg: 'show project QR' }, 668d307f52SEvan Bacon {}, 678d307f52SEvan Bacon ]); 688d307f52SEvan Bacon } else { 698d307f52SEvan Bacon logCommandsTable([ 70a7e47f4dSEvan Bacon { key: 's', msg: switchMsg }, 718d307f52SEvan Bacon {}, 728d307f52SEvan Bacon { key: 'a', msg: 'open Android', disabled: isAndroidDisabled }, 738d307f52SEvan Bacon isMac && { key: 'i', msg: 'open iOS simulator', disabled: isIosDisabled }, 748d307f52SEvan Bacon { key: 'w', msg: 'open web', disabled: isWebDisable }, 758d307f52SEvan Bacon {}, 7648103a3dSEvan Bacon { key: 'j', msg: 'open debugger' }, 778d307f52SEvan Bacon { key: 'r', msg: 'reload app' }, 788d307f52SEvan Bacon !!options.isWebSocketsEnabled && { key: 'm', msg: 'toggle menu' }, 79*20f5f217SEvan Bacon { key: 'o', msg: 'open project code in your editor' }, 808d307f52SEvan Bacon {}, 818d307f52SEvan Bacon ]); 828d307f52SEvan Bacon } 838d307f52SEvan Bacon} 848d307f52SEvan Bacon 858d307f52SEvan Baconfunction logCommandsTable( 868d307f52SEvan Bacon ui: (false | { key?: string; msg?: string; status?: string; disabled?: boolean })[] 878d307f52SEvan Bacon) { 888d307f52SEvan Bacon Log.log( 898d307f52SEvan Bacon ui 908d307f52SEvan Bacon .filter(Boolean) 918d307f52SEvan Bacon // @ts-ignore: filter doesn't work 928d307f52SEvan Bacon .map(({ key, msg, status, disabled }) => { 938d307f52SEvan Bacon if (!key) return ''; 948d307f52SEvan Bacon let view = `${BLT} `; 958d307f52SEvan Bacon if (key.length === 1) view += 'Press '; 968d307f52SEvan Bacon view += chalk`{bold ${key}} {dim │} `; 978d307f52SEvan Bacon view += msg; 988d307f52SEvan Bacon if (status) { 998d307f52SEvan Bacon view += ` ${chalk.dim(`(${chalk.italic(status)})`)}`; 1008d307f52SEvan Bacon } 1018d307f52SEvan Bacon if (disabled) { 1028d307f52SEvan Bacon view = chalk.dim(view); 1038d307f52SEvan Bacon } 1048d307f52SEvan Bacon return view; 1058d307f52SEvan Bacon }) 1068d307f52SEvan Bacon .join('\n') 1078d307f52SEvan Bacon ); 1088d307f52SEvan Bacon} 109