@@ -48,6 +48,12 @@ export type Runner<Args extends ArgsObject> = (
4848 instance : MassargCommand < Args > ,
4949) => Promise < void > | void
5050
51+ /**
52+ * Error handler callback type.
53+ * Called when an error occurs during parsing or command execution.
54+ */
55+ export type ErrorHandler = ( error : Error ) => void
56+
5157/**
5258 * A command is a named function that can be invoked with a set of options.
5359 *
@@ -79,6 +85,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject>
7985 examples : MassargExample [ ] = [ ]
8086 args : Partial < Args > = { }
8187 private _helpConfig : HelpConfig
88+ private _errorHandler ?: ErrorHandler
8289 parent ?: MassargCommand < any >
8390 optionPrefix = DEFAULT_OPT_FULL_PREFIX
8491 aliasPrefix = DEFAULT_OPT_SHORT_PREFIX
@@ -321,6 +328,46 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject>
321328 return this
322329 }
323330
331+ /**
332+ * Configure a custom error handler for this command.
333+ *
334+ * By default, errors are caught and logged to stderr with a red color.
335+ * Use this method to override the default error handling behavior.
336+ *
337+ * Note: The process will always exit with code 1 after an error, regardless of the handler.
338+ *
339+ * @example
340+ * ```ts
341+ * massarg(options)
342+ * .onError((error) => {
343+ * console.error('Custom error:', error.message)
344+ * // Log to external service, show custom UI, etc.
345+ * })
346+ * .parse()
347+ * ```
348+ */
349+ onError ( handler : ErrorHandler ) : MassargCommand < Args > {
350+ this . _errorHandler = handler
351+ return this
352+ }
353+
354+ /** Get the error handler, checking parent chain if not set locally */
355+ private get errorHandler ( ) : ErrorHandler {
356+ if ( this . _errorHandler ) {
357+ return this . _errorHandler
358+ }
359+ if ( this . parent ) {
360+ return this . parent . errorHandler
361+ }
362+ return this . defaultErrorHandler
363+ }
364+
365+ /** Default error handler that logs red error message to stderr */
366+ private defaultErrorHandler = ( e : Error ) : void => {
367+ const message = getErrorMessage ( e )
368+ console . error ( format ( message , { color : 'red' } ) )
369+ }
370+
324371 /**
325372 * Parse the given arguments and run the command or sub-commands along with the given options
326373 * and flags.
@@ -333,13 +380,30 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject>
333380 args ?: Partial < Args > ,
334381 parent ?: MassargCommand < Args > ,
335382 ) : Promise < void > | void {
383+ // Set up process-level error handlers
384+ const handleProcessError = ( error : Error ) => {
385+ this . handleError ( error )
386+ }
387+ process . on ( 'uncaughtException' , handleProcessError )
388+ process . on ( 'unhandledRejection' , ( reason ) => {
389+ const error = reason instanceof Error ? reason : new Error ( String ( reason ) )
390+ handleProcessError ( error )
391+ } )
392+
336393 try {
337394 this . getArgs ( argv , args , parent , true )
338395 } catch ( e ) {
339- this . printError ( e )
396+ this . handleError ( e )
340397 }
341398 }
342399
400+ /** Handle an error using the configured error handler, then exit */
401+ private handleError ( e : unknown ) : void {
402+ const error = e instanceof Error ? e : new Error ( getErrorMessage ( e ) )
403+ this . errorHandler ( error )
404+ process . exit ( 1 )
405+ }
406+
343407 private printError ( e : unknown ) {
344408 const message = getErrorMessage ( e )
345409 console . error ( format ( message , { color : 'red' } ) )
0 commit comments