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