11import { z } from "zod"
22import { ValidationError } from "./error"
3- import MassargOption , { MassargFlag , MassargNumber , OptionConfig , TypedOptionConfig } from "./option"
4- import { generateCommandsHelpTable , generateOptionsHelpTable , isZodError } from "./utils"
3+ import Massarg from "./massarg"
4+ import MassargOption , { TypedOptionConfig } from "./option"
5+ import { generateCommandsHelpTable , generateOptionsHelpTable , isZodError , setOrPush } from "./utils"
56
67export const CommandConfig = < RunArgs extends z . ZodType > ( args : RunArgs ) =>
78 z . object ( {
@@ -10,8 +11,10 @@ export const CommandConfig = <RunArgs extends z.ZodType>(args: RunArgs) =>
1011 aliases : z . string ( ) . array ( ) . optional ( ) ,
1112 run : z
1213 . function ( )
13- . args ( args )
14- . returns ( z . union ( [ z . promise ( z . void ( ) ) , z . void ( ) ] ) ) as z . ZodType < ( args : z . infer < RunArgs > ) => Promise < void > | void > ,
14+ . args ( args , z . any ( ) )
15+ . returns ( z . union ( [ z . promise ( z . void ( ) ) , z . void ( ) ] ) ) as z . ZodType <
16+ ( args : z . infer < RunArgs > , instance : MassargCommand < z . infer < RunArgs > > ) => Promise < void > | void
17+ > ,
1518 } )
1619
1720export type CommandConfig < T = unknown > = z . infer < ReturnType < typeof CommandConfig < z . ZodType < T > > > >
@@ -24,7 +27,10 @@ export default class MassargCommand<Args extends ArgsObject = ArgsObject> {
2427 name : string
2528 description : string
2629 aliases : string [ ]
27- private _run ?: ( options : Args ) => Promise < void > | void
30+ private _run ?: < P extends ArgsObject = Args > (
31+ options : Args ,
32+ instance : Massarg < P > ,
33+ ) => Promise < void > | void
2834 options : MassargOption [ ] = [ ]
2935 commands : MassargCommand < any > [ ] = [ ]
3036 args : Partial < Args > = { }
@@ -34,20 +40,22 @@ export default class MassargCommand<Args extends ArgsObject = ArgsObject> {
3440 this . name = options . name
3541 this . description = options . description
3642 this . aliases = options . aliases ?? [ ]
37- this . _run = options . run
43+ this . _run = options . run as typeof this . _run
3844 }
3945
4046 command < A extends ArgsObject = Args > ( config : CommandConfig < A > ) : MassargCommand < Args >
4147 command < A extends ArgsObject = Args > ( config : MassargCommand < A > ) : MassargCommand < Args >
42- command < A extends ArgsObject = Args > ( config : CommandConfig < A > | MassargCommand < A > ) : MassargCommand < Args > {
48+ command < A extends ArgsObject = Args > (
49+ config : CommandConfig < A > | MassargCommand < A > ,
50+ ) : MassargCommand < Args > {
4351 try {
4452 const command = config instanceof MassargCommand ? config : new MassargCommand ( config )
4553 this . commands . push ( command )
4654 return this
4755 } catch ( e ) {
4856 if ( isZodError ( e ) ) {
4957 throw new ValidationError ( {
50- path : [ config . name , ...e . issues [ 0 ] . path . map ( ( p ) => p . toString ( ) ) ] ,
58+ path : [ this . name , config . name , ...e . issues [ 0 ] . path . map ( ( p ) => p . toString ( ) ) ] ,
5159 code : e . issues [ 0 ] . code ,
5260 message : e . issues [ 0 ] . message ,
5361 } )
@@ -60,13 +68,14 @@ export default class MassargCommand<Args extends ArgsObject = ArgsObject> {
6068 option < T = string > ( config : TypedOptionConfig < T > ) : MassargCommand < Args >
6169 option < T = string > ( config : TypedOptionConfig < T > | MassargOption < T > ) : MassargCommand < Args > {
6270 try {
63- const option = config instanceof MassargOption ? config : MassargOption . fromTypedConfig ( config )
71+ const option =
72+ config instanceof MassargOption ? config : MassargOption . fromTypedConfig ( config )
6473 this . options . push ( option as MassargOption )
6574 return this
6675 } catch ( e ) {
6776 if ( isZodError ( e ) ) {
6877 throw new ValidationError ( {
69- path : [ config . name , ...e . issues [ 0 ] . path . map ( ( p ) => p . toString ( ) ) ] ,
78+ path : [ this . name , config . name , ...e . issues [ 0 ] . path . map ( ( p ) => p . toString ( ) ) ] ,
7079 code : e . issues [ 0 ] . code ,
7180 message : e . issues [ 0 ] . message ,
7281 } )
@@ -75,65 +84,84 @@ export default class MassargCommand<Args extends ArgsObject = ArgsObject> {
7584 }
7685 }
7786
78- main ( run : ( options : Args ) => Promise < void > | void ) : MassargCommand < Args > {
79- this . _run = run
87+ main < A extends ArgsObject = Args > (
88+ run : ( options : Args , instance : MassargCommand < A > ) => Promise < void > | void ,
89+ ) : MassargCommand < Args > {
90+ this . _run = run as typeof this . _run
8091 return this
8192 }
8293
83- parse ( argv : string [ ] , args ?: Partial < Args > ) : Promise < void > | void {
84- console . log ( "parse:" , this . name )
85- console . log ( argv )
94+ parse ( argv : string [ ] , args ?: Partial < Args > , parent ?: MassargCommand < Args > ) : Promise < void > | void {
8695 this . args ??= { }
8796 this . args = { ...this . args , ...args }
8897 let _argv = [ ...argv ]
8998 while ( _argv . length ) {
9099 const arg = _argv . shift ( ) !
91- console . log ( "parsing:" , arg , _argv )
92100 const found = this . options . some ( ( o ) => o . _isOption ( arg ) )
93101 if ( found ) {
94- console . log ( "option:" , arg , _argv )
95102 _argv = this . parseOption ( arg , _argv )
96103 continue
97104 }
98105
99106 const command = this . commands . find ( ( c ) => c . name === arg || c . aliases . includes ( arg ) )
100107 if ( command ) {
101- console . log ( "command:" , arg , _argv )
102- return command . parse ( _argv , this . args )
108+ return command . parse ( _argv , this . args , parent ?? this )
103109 }
104110 // TODO pass all un-handled args to an "args" option
105- console . log ( "Nothing to do" , arg , _argv )
106111 }
107112 if ( this . _run ) {
108- console . log ( "run:" , this . args )
109- this . _run ( { ...args , ...this . args } as Args )
113+ this . _run ( { ...args , ...this . args } as Args , parent ?? this )
110114 }
111115 }
112116
113- private parseOption ( arg : string , argv : string [ ] ) : string [ ] {
117+ private parseOption ( arg : string , argv : string [ ] ) {
114118 const option = this . options . find ( ( o ) => o . _match ( arg ) )
115-
116119 if ( ! option ) {
117- // TODO create custom error object
118- throw new Error ( `Unknown option ${ arg } ` )
119- }
120- const res = option . valueFromArgv ( [ arg , ...argv ] )
121- console . log ( "option class name" , option . constructor . name )
122- if ( option . isArray ) {
123- this . args [ res . key as keyof Args ] ??= [ ] as Args [ keyof Args ]
124- const _a = this . args [ res . key as keyof Args ] as unknown [ ]
125- _a . push ( res . value ) as Args [ keyof Args ]
126- } else {
127- this . args [ res . key as keyof Args ] = res . value as Args [ keyof Args ]
120+ throw new ValidationError ( {
121+ path : [ arg ] ,
122+ code : "unknown_option" ,
123+ message : "Unknown option" ,
124+ } )
128125 }
129- console . log ( "option response:" , { value : res . value , argv : res . argv } )
126+ const res = option . _parseDetails ( [ arg , ...argv ] )
127+ this . args [ res . key as keyof Args ] = setOrPush < Args [ keyof Args ] > (
128+ res . value ,
129+ this . args [ res . key as keyof Args ] ,
130+ option . isArray ,
131+ )
130132 return res . argv
131133 }
132134
133135 getArgs ( argv : string [ ] ) : Args {
134- console . log ( "getArgs:" , this . name )
135- console . log ( argv )
136- return { } as Args
136+ let args : Args = { } as Args
137+ let _argv = [ ...argv ]
138+ while ( _argv . length ) {
139+ const arg = _argv . shift ( ) !
140+ const found = this . options . some ( ( o ) => o . _isOption ( arg ) )
141+ if ( found ) {
142+ const option = this . options . find ( ( o ) => o . _match ( arg ) )
143+ if ( ! option ) {
144+ throw new ValidationError ( {
145+ path : [ arg ] ,
146+ code : "unknown_option" ,
147+ message : "Unknown option" ,
148+ } )
149+ }
150+ const res = option . _parseDetails ( argv )
151+ args [ res . key as keyof Args ] = setOrPush < Args [ keyof Args ] > (
152+ res . value ,
153+ args [ res . key as keyof Args ] ,
154+ option . isArray ,
155+ )
156+ continue
157+ }
158+
159+ const command = this . commands . find ( ( c ) => c . name === arg || c . aliases . includes ( arg ) )
160+ if ( command ) {
161+ break
162+ }
163+ }
164+ return args
137165 }
138166
139167 helpString ( ) : string {
@@ -151,6 +179,10 @@ export default class MassargCommand<Args extends ArgsObject = ArgsObject> {
151179 . filter ( ( s ) => typeof s === "string" )
152180 . join ( "\n" )
153181 }
182+
183+ printHelp ( ) : void {
184+ console . log ( this . helpString ( ) )
185+ }
154186}
155187
156188export { MassargCommand }
0 commit comments