@@ -45,7 +45,7 @@ import {
4545import { mapParametersToArguments , parseParameters } from "./parameters" ;
4646import { locales } from "./locales" ;
4747
48- export const Root = Symbol ( " Root") ;
48+ export const Root = Symbol . for ( "Clerc. Root") ;
4949export type RootType = typeof Root ;
5050
5151export class Clerc < C extends CommandRecord = { } > {
@@ -57,6 +57,7 @@ export class Clerc<C extends CommandRecord = {}> {
5757 #commandEmitter = new LiteEmit < MakeEventMap < C > > ( ) ;
5858 #usedNames = new Set < string | RootType > ( ) ;
5959 #argv: string [ ] | undefined ;
60+ #errorHandlers = [ ] as ( ( err : any ) => void ) [ ] ;
6061
6162 #isOtherMethodCalled = false ;
6263 #defaultLocale = "en" ;
@@ -162,7 +163,7 @@ export class Clerc<C extends CommandRecord = {}> {
162163
163164 /**
164165 * Set the Locale
165- * It's recommended to call this method once after you created the Clerc instance.
166+ * You must call this method once after you created the Clerc instance.
166167 * @param locale
167168 * @returns
168169 * @example
@@ -180,7 +181,7 @@ export class Clerc<C extends CommandRecord = {}> {
180181
181182 /**
182183 * Set the fallback Locale
183- * It's recommended to call this method once after you created the Clerc instance.
184+ * You must call this method once after you created the Clerc instance.
184185 * @param fallbackLocale
185186 * @returns
186187 * @example
@@ -196,6 +197,21 @@ export class Clerc<C extends CommandRecord = {}> {
196197 return this ;
197198 }
198199
200+ /**
201+ * Register a error handler
202+ * @param handler
203+ * @returns
204+ * @example
205+ * ```ts
206+ * Clerc.create()
207+ * .errorHandler((err) => { console.log(err); })
208+ * ```
209+ */
210+ errorHandler ( handler : ( err : any ) => void ) {
211+ this . #errorHandlers. push ( handler ) ;
212+ return this ;
213+ }
214+
199215 /**
200216 * Register a command
201217 * @param name
@@ -356,87 +372,74 @@ export class Clerc<C extends CommandRecord = {}> {
356372 }
357373 }
358374
359- /**
360- * Run matched command
361- * @returns
362- * @example
363- * ```ts
364- * Clerc.create()
365- * .parse({ run: false })
366- * .runMatchedCommand()
367- * ```
368- */
369- runMatchedCommand ( ) {
375+ #getContext( getCommand : ( ) => ReturnType < typeof resolveCommand > ) {
376+ const argv = this . #argv! ;
377+ const [ command , called ] = getCommand ( ) ;
378+ const isCommandResolved = ! ! command ;
379+ // [...argv] is a workaround since TypeFlag modifies argv
380+ const parsed = typeFlag ( command ?. flags || { } , [ ...argv ] ) ;
381+ const { _ : args , flags, unknownFlags } = parsed ;
382+ let parameters = ! isCommandResolved || command . name === Root ? args : args . slice ( command . name . split ( " " ) . length ) ;
383+ let commandParameters = command ?. parameters || [ ] ;
384+ // eof handle
385+ const hasEof = commandParameters . indexOf ( "--" ) ;
386+ const eofParameters = commandParameters . slice ( hasEof + 1 ) || [ ] ;
387+ const mapping : Record < string , string | string [ ] > = Object . create ( null ) ;
388+ // Support `--` eof parameters
389+ if ( hasEof > - 1 && eofParameters . length > 0 ) {
390+ commandParameters = commandParameters . slice ( 0 , hasEof ) ;
391+ const eofArguments = args [ "--" ] ;
392+ parameters = parameters . slice ( 0 , - eofArguments . length || undefined ) ;
393+
394+ mapParametersToArguments (
395+ mapping ,
396+ parseParameters ( commandParameters ) ,
397+ parameters ,
398+ ) ;
399+ mapParametersToArguments (
400+ mapping ,
401+ parseParameters ( eofParameters ) ,
402+ eofArguments ,
403+ ) ;
404+ } else {
405+ mapParametersToArguments (
406+ mapping ,
407+ parseParameters ( commandParameters ) ,
408+ parameters ,
409+ ) ;
410+ }
411+ const mergedFlags = { ...flags , ...unknownFlags } ;
412+ const context : InspectorContext | HandlerContext = {
413+ name : command ?. name as any ,
414+ called : Array . isArray ( called ) ? called . join ( " " ) : called ,
415+ resolved : isCommandResolved as any ,
416+ hasRootOrAlias : this . #hasRootOrAlias,
417+ hasRoot : this . #hasRoot,
418+ raw : { ...parsed , parameters, mergedFlags } ,
419+ parameters : mapping ,
420+ flags,
421+ unknownFlags,
422+ cli : this as any ,
423+ } ;
424+ return context ;
425+ }
426+
427+ #runMatchedCommand( ) {
370428 this . #otherMethodCaled( ) ;
371429 const { t } = this . i18n ;
372430 const argv = this . #argv;
373431 if ( ! argv ) {
374- throw new Error ( "cli.parse() must be called." ) ;
432+ throw new Error ( t ( "core.cliParseMustBeCalled" ) ) ;
375433 }
376434 const name = resolveParametersBeforeFlag ( argv ) ;
377435 const stringName = name . join ( " " ) ;
378436 const getCommand = ( ) => resolveCommand ( this . #commands, name , t ) ;
379- const mapErrors = [ ] as ( Error | undefined ) [ ] ;
380- const getContext = ( ) => {
381- mapErrors . length = 0 ;
382- const [ command , called ] = getCommand ( ) ;
383- const isCommandResolved = ! ! command ;
384- // [...argv] is a workaround since TypeFlag modifies argv
385- const parsed = typeFlag ( command ?. flags || { } , [ ...argv ] ) ;
386- const { _ : args , flags, unknownFlags } = parsed ;
387- let parameters = ! isCommandResolved || command . name === Root ? args : args . slice ( command . name . split ( " " ) . length ) ;
388- let commandParameters = command ?. parameters || [ ] ;
389- // eof handle
390- const hasEof = commandParameters . indexOf ( "--" ) ;
391- const eofParameters = commandParameters . slice ( hasEof + 1 ) || [ ] ;
392- const mapping : Record < string , string | string [ ] > = Object . create ( null ) ;
393- // Support `--` eof parameters
394- if ( hasEof > - 1 && eofParameters . length > 0 ) {
395- commandParameters = commandParameters . slice ( 0 , hasEof ) ;
396- const eofArguments = args [ "--" ] ;
397- parameters = parameters . slice ( 0 , - eofArguments . length || undefined ) ;
398-
399- mapErrors . push ( mapParametersToArguments (
400- mapping ,
401- parseParameters ( commandParameters ) ,
402- parameters ,
403- ) ) ;
404- mapErrors . push ( mapParametersToArguments (
405- mapping ,
406- parseParameters ( eofParameters ) ,
407- eofArguments ,
408- ) ) ;
409- } else {
410- mapErrors . push ( mapParametersToArguments (
411- mapping ,
412- parseParameters ( commandParameters ) ,
413- parameters ,
414- ) ) ;
415- }
416- const mergedFlags = { ...flags , ...unknownFlags } ;
417- const context : InspectorContext | HandlerContext = {
418- name : command ?. name as any ,
419- called : Array . isArray ( called ) ? called . join ( " " ) : called ,
420- resolved : isCommandResolved as any ,
421- hasRootOrAlias : this . #hasRootOrAlias,
422- hasRoot : this . #hasRoot,
423- raw : { ...parsed , parameters, mergedFlags } ,
424- parameters : mapping ,
425- flags,
426- unknownFlags,
427- cli : this as any ,
428- } ;
429- return context ;
430- } ;
437+ const getContext = ( ) => this . #getContext( getCommand ) ;
431438 const emitHandler : Inspector = {
432439 enforce : "post" ,
433440 fn : ( ) => {
434441 const [ command ] = getCommand ( ) ;
435442 const handlerContext = getContext ( ) ;
436- const errors = mapErrors . filter ( Boolean ) as Error [ ] ;
437- if ( errors . length > 0 ) {
438- throw errors [ 0 ] ;
439- }
440443 if ( ! command ) {
441444 if ( stringName ) {
442445 throw new NoSuchCommandError ( stringName , t ) ;
@@ -450,6 +453,28 @@ export class Clerc<C extends CommandRecord = {}> {
450453 const inspectors = [ ...this . #inspectors, emitHandler ] ;
451454 const callInspector = compose ( inspectors ) ;
452455 callInspector ( getContext ) ;
456+ }
457+
458+ /**
459+ * Run matched command
460+ * @returns
461+ * @example
462+ * ```ts
463+ * Clerc.create()
464+ * .parse({ run: false })
465+ * .runMatchedCommand()
466+ * ```
467+ */
468+ runMatchedCommand ( ) {
469+ try {
470+ this . #runMatchedCommand( ) ;
471+ } catch ( e ) {
472+ if ( this . #errorHandlers. length > 0 ) {
473+ this . #errorHandlers. forEach ( cb => cb ( e ) ) ;
474+ } else {
475+ throw e ;
476+ }
477+ }
453478 return this ;
454479 }
455480}
0 commit comments