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