1import { AssertionError } from 'assert'; 2import chalk from 'chalk'; 3 4import { exit } from '../log'; 5 6const ERROR_PREFIX = 'Error: '; 7 8/** 9 * 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. 10 */ 11export class CommandError extends Error { 12 name = 'CommandError'; 13 readonly isCommandError = true; 14 15 constructor( 16 public code: string, 17 message: string = '' 18 ) { 19 super(''); 20 // If e.toString() was called to get `message` we don't want it to look 21 // like "Error: Error:". 22 if (message.startsWith(ERROR_PREFIX)) { 23 message = message.substring(ERROR_PREFIX.length); 24 } 25 26 this.message = message || code; 27 } 28} 29 30export class AbortCommandError extends CommandError { 31 constructor() { 32 super('ABORTED', 'Interactive prompt was cancelled.'); 33 } 34} 35 36/** 37 * Used to end a CLI process without printing a stack trace in the Expo CLI. Should be used in favor of `process.exit`. 38 */ 39export class SilentError extends CommandError { 40 constructor(messageOrError?: string | Error) { 41 const message = 42 (typeof messageOrError === 'string' ? messageOrError : messageOrError?.message) ?? 43 'This error should fail silently in the CLI'; 44 super('SILENT', message); 45 if (typeof messageOrError !== 'string') { 46 // forward the props of the incoming error for tests or processes outside of expo-cli that use expo cli internals. 47 this.stack = messageOrError?.stack ?? this.stack; 48 this.name = messageOrError?.name ?? this.name; 49 } 50 } 51} 52 53export function logCmdError(error: Error): never { 54 if (error instanceof AbortCommandError || error instanceof SilentError) { 55 // Do nothing, this is used for prompts or other cases that were custom logged. 56 process.exit(0); 57 } else if ( 58 error instanceof CommandError || 59 error instanceof AssertionError || 60 error.name === 'ApiV2Error' || 61 error.name === 'ConfigError' 62 ) { 63 // Print the stack trace in debug mode only. 64 exit(error); 65 } 66 67 const errorDetails = error.stack ? '\n' + chalk.gray(error.stack) : ''; 68 69 exit(chalk.red(error.toString()) + errorDetails); 70} 71 72/** This should never be thrown in production. */ 73export class UnimplementedError extends Error { 74 constructor() { 75 super('Unimplemented'); 76 this.name = 'UnimplementedError'; 77 } 78} 79