xref: /expo/packages/@expo/cli/src/utils/ora.ts (revision af644ed4)
1import chalk from 'chalk';
2import oraReal, { Ora } from 'ora';
3
4// import * as Log from '../log';
5import { env } from './env';
6
7const logReal = console.log;
8const infoReal = console.info;
9const warnReal = console.warn;
10const errorReal = console.error;
11
12const runningSpinners: oraReal.Ora[] = [];
13
14export function getAllSpinners() {
15  return runningSpinners;
16}
17
18/**
19 * A custom ora spinner that sends the stream to stdout in CI, non-TTY, or expo's non-interactive flag instead of stderr (the default).
20 *
21 * @param options
22 * @returns
23 */
24export function ora(options?: oraReal.Options | string): oraReal.Ora {
25  const inputOptions = typeof options === 'string' ? { text: options } : options || {};
26  const disabled = env.CI || env.EXPO_DEBUG;
27  const spinner = oraReal({
28    // Ensure our non-interactive mode emulates CI mode.
29    isEnabled: !disabled,
30    // In non-interactive mode, send the stream to stdout so it prevents looking like an error.
31    stream: disabled ? process.stdout : process.stderr,
32    ...inputOptions,
33  });
34
35  const oraStart = spinner.start.bind(spinner);
36  const oraStop = spinner.stop.bind(spinner);
37  const oraStopAndPersist = spinner.stopAndPersist.bind(spinner);
38
39  const logWrap = (method: any, args: any[]): void => {
40    oraStop();
41    method(...args);
42    spinner.start();
43  };
44
45  const wrapNativeLogs = (): void => {
46    console.log = (...args: any) => logWrap(logReal, args);
47    console.info = (...args: any) => logWrap(infoReal, args);
48    console.warn = (...args: any) => logWrap(warnReal, args);
49    console.error = (...args: any) => logWrap(errorReal, args);
50
51    runningSpinners.push(spinner);
52  };
53
54  const resetNativeLogs = (): void => {
55    console.log = logReal;
56    console.info = infoReal;
57    console.warn = warnReal;
58    console.error = errorReal;
59
60    const index = runningSpinners.indexOf(spinner);
61    if (index >= 0) {
62      runningSpinners.splice(index, 1);
63    }
64  };
65
66  spinner.start = (text): Ora => {
67    wrapNativeLogs();
68    return oraStart(text);
69  };
70
71  spinner.stopAndPersist = (options): Ora => {
72    const result = oraStopAndPersist(options);
73    resetNativeLogs();
74    return result;
75  };
76
77  spinner.stop = (): Ora => {
78    const result = oraStop();
79    resetNativeLogs();
80    return result;
81  };
82
83  // Always make the central logging module aware of the current spinner
84  // Log.setSpinner(spinner);
85
86  return spinner;
87}
88
89/**
90 * Create a unified section spinner.
91 *
92 * @param title
93 * @returns
94 */
95export function logNewSection(title: string) {
96  const spinner = ora(chalk.bold(title));
97  // Prevent the spinner from clashing with debug logs
98  spinner.start();
99  return spinner;
100}
101