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