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 const switchMsg = `switch to ${options.devClient === false ? 'development build' : 'Expo Go'}`; 45 const target = options.devClient === false ? `Expo Go` : 'development build'; 46 47 Log.log(); 48 Log.log(printItem(chalk`Using {cyan ${target}}`)); 49 50 if (verbose) { 51 logCommandsTable([ 52 { key: 's', msg: switchMsg }, 53 {}, 54 { key: 'a', msg: 'open Android', disabled: isAndroidDisabled }, 55 { key: 'shift+a', msg: 'select a device or emulator', disabled: isAndroidDisabled }, 56 isMac && { key: 'i', msg: 'open iOS simulator', disabled: isIosDisabled }, 57 isMac && { key: 'shift+i', msg: 'select a simulator', disabled: isIosDisabled }, 58 { key: 'w', msg: 'open web', disabled: isWebDisable }, 59 {}, 60 { key: 'r', msg: 'reload app' }, 61 !!options.isWebSocketsEnabled && { key: 'j', msg: 'open debugger' }, 62 !!options.isWebSocketsEnabled && { key: 'm', msg: 'toggle menu' }, 63 !!options.isWebSocketsEnabled && { key: 'shift+m', msg: 'more tools' }, 64 { key: 'o', msg: 'open project code in your editor' }, 65 { key: 'c', msg: 'show project QR' }, 66 {}, 67 ]); 68 } else { 69 logCommandsTable([ 70 { key: 's', msg: switchMsg }, 71 {}, 72 { key: 'a', msg: 'open Android', disabled: isAndroidDisabled }, 73 isMac && { key: 'i', msg: 'open iOS simulator', disabled: isIosDisabled }, 74 { key: 'w', msg: 'open web', disabled: isWebDisable }, 75 {}, 76 { key: 'j', msg: 'open debugger' }, 77 { key: 'r', msg: 'reload app' }, 78 !!options.isWebSocketsEnabled && { key: 'm', msg: 'toggle menu' }, 79 { key: 'o', msg: 'open project code in your editor' }, 80 {}, 81 ]); 82 } 83} 84 85function logCommandsTable( 86 ui: (false | { key?: string; msg?: string; status?: string; disabled?: boolean })[] 87) { 88 Log.log( 89 ui 90 .filter(Boolean) 91 // @ts-ignore: filter doesn't work 92 .map(({ key, msg, status, disabled }) => { 93 if (!key) return ''; 94 let view = `${BLT} `; 95 if (key.length === 1) view += 'Press '; 96 view += chalk`{bold ${key}} {dim │} `; 97 view += msg; 98 if (status) { 99 view += ` ${chalk.dim(`(${chalk.italic(status)})`)}`; 100 } 101 if (disabled) { 102 view = chalk.dim(view); 103 } 104 return view; 105 }) 106 .join('\n') 107 ); 108} 109