xref: /expo/packages/@expo/cli/src/utils/errors.ts (revision 8a424beb)
18d307f52SEvan Baconimport { AssertionError } from 'assert';
28d307f52SEvan Baconimport chalk from 'chalk';
38d307f52SEvan Bacon
48d307f52SEvan Baconimport { exit } from '../log';
58d307f52SEvan Bacon
68d307f52SEvan Baconconst ERROR_PREFIX = 'Error: ';
78d307f52SEvan Bacon
88d307f52SEvan Bacon/**
98d307f52SEvan Bacon * General error, formatted as a message in red text when caught by expo-cli (no stack trace is printed). Should be used in favor of `log.error()` in most cases.
108d307f52SEvan Bacon */
118d307f52SEvan Baconexport class CommandError extends Error {
128d307f52SEvan Bacon  name = 'CommandError';
138d307f52SEvan Bacon  readonly isCommandError = true;
148d307f52SEvan Bacon
15*8a424bebSJames Ide  constructor(
16*8a424bebSJames Ide    public code: string,
17*8a424bebSJames Ide    message: string = ''
18*8a424bebSJames Ide  ) {
198d307f52SEvan Bacon    super('');
208d307f52SEvan Bacon    // If e.toString() was called to get `message` we don't want it to look
218d307f52SEvan Bacon    // like "Error: Error:".
228d307f52SEvan Bacon    if (message.startsWith(ERROR_PREFIX)) {
238d307f52SEvan Bacon      message = message.substring(ERROR_PREFIX.length);
248d307f52SEvan Bacon    }
258d307f52SEvan Bacon
268d307f52SEvan Bacon    this.message = message || code;
278d307f52SEvan Bacon  }
288d307f52SEvan Bacon}
298d307f52SEvan Bacon
308d307f52SEvan Baconexport class AbortCommandError extends CommandError {
318d307f52SEvan Bacon  constructor() {
328d307f52SEvan Bacon    super('ABORTED', 'Interactive prompt was cancelled.');
338d307f52SEvan Bacon  }
348d307f52SEvan Bacon}
358d307f52SEvan Bacon
368d307f52SEvan Bacon/**
378d307f52SEvan Bacon * Used to end a CLI process without printing a stack trace in the Expo CLI. Should be used in favor of `process.exit`.
388d307f52SEvan Bacon */
398d307f52SEvan Baconexport class SilentError extends CommandError {
408d307f52SEvan Bacon  constructor(messageOrError?: string | Error) {
418d307f52SEvan Bacon    const message =
428d307f52SEvan Bacon      (typeof messageOrError === 'string' ? messageOrError : messageOrError?.message) ??
438d307f52SEvan Bacon      'This error should fail silently in the CLI';
448d307f52SEvan Bacon    super('SILENT', message);
458d307f52SEvan Bacon    if (typeof messageOrError !== 'string') {
468d307f52SEvan Bacon      // forward the props of the incoming error for tests or processes outside of expo-cli that use expo cli internals.
478d307f52SEvan Bacon      this.stack = messageOrError?.stack ?? this.stack;
488d307f52SEvan Bacon      this.name = messageOrError?.name ?? this.name;
498d307f52SEvan Bacon    }
508d307f52SEvan Bacon  }
518d307f52SEvan Bacon}
528d307f52SEvan Bacon
538d307f52SEvan Baconexport function logCmdError(error: Error): never {
548d307f52SEvan Bacon  if (error instanceof AbortCommandError || error instanceof SilentError) {
558d307f52SEvan Bacon    // Do nothing, this is used for prompts or other cases that were custom logged.
568d307f52SEvan Bacon    process.exit(0);
578d307f52SEvan Bacon  } else if (
588d307f52SEvan Bacon    error instanceof CommandError ||
598d307f52SEvan Bacon    error instanceof AssertionError ||
60ca88eb6eSBartosz Kaszubowski    error.name === 'ApiV2Error' ||
61ca88eb6eSBartosz Kaszubowski    error.name === 'ConfigError'
628d307f52SEvan Bacon  ) {
638d307f52SEvan Bacon    // Print the stack trace in debug mode only.
648d307f52SEvan Bacon    exit(error);
658d307f52SEvan Bacon  }
668d307f52SEvan Bacon
67ca88eb6eSBartosz Kaszubowski  const errorDetails = error.stack ? '\n' + chalk.gray(error.stack) : '';
68ca88eb6eSBartosz Kaszubowski
69ca88eb6eSBartosz Kaszubowski  exit(chalk.red(error.toString()) + errorDetails);
708d307f52SEvan Bacon}
718d307f52SEvan Bacon
728d307f52SEvan Bacon/** This should never be thrown in production. */
738d307f52SEvan Baconexport class UnimplementedError extends Error {
748d307f52SEvan Bacon  constructor() {
758d307f52SEvan Bacon    super('Unimplemented');
768d307f52SEvan Bacon    this.name = 'UnimplementedError';
778d307f52SEvan Bacon  }
788d307f52SEvan Bacon}
79