1import { ExpoConfig } from '@expo/config-types'; 2import chalk from 'chalk'; 3import qrcode from 'qrcode-terminal'; 4import wrapAnsi from 'wrap-ansi'; 5 6import * as Log from '../../log'; 7 8export const BLT = '\u203A'; 9 10export type StartOptions = { 11 isWebSocketsEnabled?: boolean; 12 devClient?: boolean; 13 reset?: boolean; 14 nonPersistent?: boolean; 15 maxWorkers?: number; 16 platforms?: ExpoConfig['platforms']; 17}; 18 19export const printHelp = (): void => { 20 logCommandsTable([{ key: '?', msg: 'show all commands' }]); 21}; 22 23/** Print the world famous 'Expo QR Code'. */ 24export function printQRCode(url: string) { 25 qrcode.generate(url, { small: true }, (code) => Log.log(code)); 26} 27 28export const getTerminalColumns = () => process.stdout.columns || 80; 29export const printItem = (text: string): string => 30 `${BLT} ` + wrapAnsi(text, getTerminalColumns()).trimStart(); 31 32export function printUsage( 33 options: Pick<StartOptions, 'devClient' | 'isWebSocketsEnabled' | 'platforms'>, 34 { verbose }: { verbose: boolean } 35) { 36 const isMac = process.platform === 'darwin'; 37 38 const { platforms = ['ios', 'android', 'web'] } = options; 39 40 const isAndroidDisabled = !platforms.includes('android'); 41 const isIosDisabled = !platforms.includes('ios'); 42 const isWebDisable = !platforms.includes('web'); 43 44 if (verbose) { 45 logCommandsTable([ 46 {}, 47 { key: 'a', msg: 'open Android', disabled: isAndroidDisabled }, 48 { key: 'shift+a', msg: 'select a device or emulator', disabled: isAndroidDisabled }, 49 isMac && { key: 'i', msg: 'open iOS simulator', disabled: isIosDisabled }, 50 isMac && { key: 'shift+i', msg: 'select a simulator', disabled: isIosDisabled }, 51 { key: 'w', msg: 'open web', disabled: isWebDisable }, 52 {}, 53 { key: 'r', msg: 'reload app' }, 54 !!options.isWebSocketsEnabled && { key: 'm', msg: 'toggle menu' }, 55 !!options.isWebSocketsEnabled && { key: 'shift+m', msg: 'more tools' }, 56 !!options.isWebSocketsEnabled && { key: 'j', msg: 'open JavaScript inspector for Hermes' }, 57 { key: 'o', msg: 'open project code in your editor' }, 58 { key: 'c', msg: 'show project QR' }, 59 {}, 60 ]); 61 } else { 62 logCommandsTable([ 63 {}, 64 { key: 'a', msg: 'open Android', disabled: isAndroidDisabled }, 65 isMac && { key: 'i', msg: 'open iOS simulator', disabled: isIosDisabled }, 66 { key: 'w', msg: 'open web', disabled: isWebDisable }, 67 {}, 68 { key: 'r', msg: 'reload app' }, 69 !!options.isWebSocketsEnabled && { key: 'm', msg: 'toggle menu' }, 70 {}, 71 ]); 72 } 73} 74 75function logCommandsTable( 76 ui: (false | { key?: string; msg?: string; status?: string; disabled?: boolean })[] 77) { 78 Log.log( 79 ui 80 .filter(Boolean) 81 // @ts-ignore: filter doesn't work 82 .map(({ key, msg, status, disabled }) => { 83 if (!key) return ''; 84 let view = `${BLT} `; 85 if (key.length === 1) view += 'Press '; 86 view += chalk`{bold ${key}} {dim │} `; 87 view += msg; 88 if (status) { 89 view += ` ${chalk.dim(`(${chalk.italic(status)})`)}`; 90 } 91 if (disabled) { 92 view = chalk.dim(view); 93 } 94 return view; 95 }) 96 .join('\n') 97 ); 98} 99