1
+ import process from 'node:process' ;
2
+
3
+ export default function createCommandLineArgumentsHandler ( validArguments ) {
4
+ const validTypes = [ 'json' , 'string' , 'number' , 'boolean' ] ;
5
+ const argumentInfo = { } ;
6
+ const shortArgsMap = { } ;
7
+ const defaultTemplate = { } ;
8
+ {
9
+ const inputKeys = Object . keys ( validArguments ) ;
10
+ for ( let i = 0 ; i < inputKeys . length ; i ++ ) {
11
+ const key = inputKeys [ i ] ;
12
+ const firstCode = key [ 0 ] . charCodeAt ( 0 ) ;
13
+ if ( ( firstCode < 65 || firstCode > 90 ) && ( firstCode < 97 || firstCode > 122 ) )
14
+ continue ;
15
+ const argument = validArguments [ key ] ;
16
+ if ( argument . type && validTypes . includes ( argument . type ) ) {
17
+ argumentInfo [ key ] = { } ;
18
+ argumentInfo [ key ] . type = argument . type ;
19
+ argumentInfo [ key ] . hasShortKey = argument . shortKey ?? ( argument . type === 'boolean' ) ;
20
+ argumentInfo [ key ] . takesParameter = ( argument . type !== 'boolean' ? true : argument . takesParameter ?? false ) ;
21
+ argumentInfo [ key ] . acceptsPropertySyntax = argument . propertySyntax ;
22
+ if ( argument . default ) {
23
+ defaultTemplate [ key ] = argument . default ;
24
+ }
25
+ }
26
+ }
27
+ validArguments = null ;
28
+ }
29
+ const argumentKeys = Object . keys ( argumentInfo ) ;
30
+ for ( let i = 0 ; i < argumentKeys . length ; i ++ ) {
31
+ const key = argumentKeys [ i ] ;
32
+ if ( ! argumentInfo [ key ] . hasShortKey )
33
+ continue ;
34
+ let shortKey = null ;
35
+ let shortKeyCandidate = key [ 0 ] . toLowerCase ( ) ;
36
+ for ( let j = 0 ; j < 26 ; j ++ ) {
37
+ if ( ! shortArgsMap [ shortKeyCandidate ] ) {
38
+ shortArgsMap [ shortKeyCandidate ] = key ;
39
+ shortKey = shortKeyCandidate ;
40
+ break ;
41
+ } else if ( ! shortArgsMap [ shortKeyCandidate . toUpperCase ( ) ] ) {
42
+ shortArgsMap [ shortKeyCandidate . toUpperCase ( ) ] = key ;
43
+ shortKey = shortKeyCandidate . toUpperCase ( ) ;
44
+ break ;
45
+ }
46
+ shortKeyCandidate = String . fromCharCode ( ( ( shortKeyCandidate . charCodeAt ( 0 ) - 95 ) % 26 ) + 96 ) ;
47
+ console . log ( shortKeyCandidate ) ;
48
+ }
49
+ if ( ! shortKey )
50
+ throw new Error ( `Could not assign short key for argument: ${ key } ` ) ;
51
+ }
52
+
53
+ function checkForSplitValue ( value , args , index ) {
54
+ if ( value [ 0 ] == "'" ) {
55
+ return value [ value . length - 1 ] !== "'" ;
56
+ } else if ( value [ 0 ] == '"' ) {
57
+ return value [ value . length - 1 ] !== '"' ;
58
+ }
59
+ return false ;
60
+ }
61
+
62
+ function parseBooleanArgument ( key , args , index , options , value ) {
63
+ if ( value !== null && ! argumentInfo [ key ] . takesParameter ) {
64
+ throw new Error ( `Invalid option: ${ key } ` ) ;
65
+ } else if ( value === null && ! argumentInfo [ key ] . takesParameter ) {
66
+ options [ key ] = true ;
67
+ return 0 ;
68
+ } else if ( argumentInfo [ key ] . takesParameter ) {
69
+ let increment = 0 ;
70
+ if ( value === null && args . length > index + 1 ) {
71
+ value = args [ index + 1 ] ;
72
+ increment = 1 ;
73
+ } else if ( value === null ) {
74
+ throw new Error ( `Invalid option: ${ key } ` ) ;
75
+ } else if ( checkForSplitValue ( value ) ) {
76
+ do {
77
+ if ( args . length <= index + increment )
78
+ throw new Error ( `Unclosed option value: ${ key } ` ) ;
79
+ value += ' ' + args [ index + 1 ] ;
80
+ increment ++ ;
81
+ } while ( checkForSplitValue ( value ) ) ;
82
+ value = value . slice ( 1 , - 1 ) ;
83
+ }
84
+ options [ key ] = value ;
85
+ return increment ;
86
+ } else {
87
+ throw new Error ( `Invalid option: ${ key } ` ) ;
88
+ }
89
+ }
90
+
91
+ function parseNumberArgument ( key , args , index , options , value ) {
92
+ let increment = 0 ;
93
+ if ( value === null && args . length > index + 1 ) {
94
+ value = args [ index + 1 ] ;
95
+ increment = 1 ;
96
+ }
97
+ if ( value === null )
98
+ throw new Error ( `Invalid option: ${ key } ` ) ;
99
+ if ( checkForSplitValue ( value ) )
100
+ throw new Error ( `Unclosed option value: ${ key } ` ) ;
101
+ if ( value . startsWith ( "0x" ) ) {
102
+ options [ key ] = parseInt ( value . slice ( 2 ) , 16 ) ;
103
+ } else if ( value . startsWith ( "0o" ) ) {
104
+ options [ key ] = parseInt ( value . slice ( 2 ) , 8 ) ;
105
+ } else if ( value . startsWith ( "0b" ) ) {
106
+ options [ key ] = parseInt ( value . slice ( 2 ) , 2 ) ;
107
+ } else {
108
+ if ( value . startsWith ( "0d" ) ) {
109
+ options [ key ] = parseInt ( value . slice ( 2 ) , 10 ) ;
110
+ } else options [ key ] = parseFloat ( value ) ;
111
+ }
112
+ return increment ;
113
+ }
114
+
115
+ function parseStringArgument ( key , args , index , options , value ) {
116
+ let increment = 0 ;
117
+ if ( value === null && args . length > index + 1 ) {
118
+ value = args [ index + 1 ] ;
119
+ increment = 1 ;
120
+ }
121
+ if ( value === null )
122
+ throw new Error ( `Invalid option: ${ key } ` ) ;
123
+ if ( checkForSplitValue ( value ) ) {
124
+ do {
125
+ if ( args . length <= index + increment )
126
+ throw new Error ( `Unclosed option value: ${ key } ` ) ;
127
+ value += ' ' + args [ index + 1 ] ;
128
+ increment ++ ;
129
+ } while ( checkForSplitValue ( value ) ) ;
130
+ value = value . slice ( 1 , - 1 ) ;
131
+ }
132
+ options [ key ] = value ;
133
+ return increment ;
134
+ }
135
+
136
+ function parseJsonArgument ( key , args , index , options , value ) {
137
+ let increment = 0 ;
138
+ if ( value === null && args . length > index + 1 ) {
139
+ value = args [ index + 1 ] ;
140
+ increment = 1 ;
141
+ }
142
+ if ( value === null )
143
+ throw new Error ( `Invalid option: ${ key } ` ) ;
144
+ if ( checkForSplitValue ( value ) ) {
145
+ do {
146
+ if ( args . length <= index + increment )
147
+ throw new Error ( `Unclosed option value: ${ key } ` ) ;
148
+ value += ' ' + args [ index + 1 ] ;
149
+ increment ++ ;
150
+ } while ( checkForSplitValue ( value ) ) ;
151
+ value = value . slice ( 1 , - 1 ) ;
152
+ }
153
+ options [ key ] = JSON . parse ( value . replaceAll ( "'" , "\"" ) ) ;
154
+ return increment ;
155
+ }
156
+
157
+
158
+ return function ( ) {
159
+ const args = process . argv . slice ( 2 ) ;
160
+ const parsedArgs = { args : [ ] , options : Object . assign ( { } , defaultTemplate ) } ;
161
+
162
+ for ( let i = 0 ; i < args . length ; i ++ ) {
163
+ const arg = args [ i ] ;
164
+ if ( arg . startsWith ( '--' ) ) {
165
+ const longArg = arg . slice ( 2 ) ;
166
+ let key = longArg ;
167
+ let value = null ;
168
+ if ( ! argumentInfo [ key . replace ( / - / g, '_' ) ] ) {
169
+ let splitproperty = longArg . split ( "=" ) ;
170
+ if ( splitproperty . length > 1 ) {
171
+ key = splitproperty [ 0 ] ;
172
+ value = splitproperty [ 1 ] ;
173
+ } else {
174
+ throw new Error ( `Invalid option: ${ key } ` ) ;
175
+ }
176
+ if ( ! argumentInfo [ key ] )
177
+ throw new Error ( `Invalid option: ${ key } ` ) ;
178
+ if ( ! argumentInfo [ key ] . acceptsPropertySyntax )
179
+ throw new Error ( `Invalid property syntax for option: ${ key } ` ) ;
180
+ }
181
+ if ( key . indexOf ( '_' ) !== - 1 )
182
+ throw new Error ( `Invalid option: ${ key } ` ) ;
183
+ key = key . replace ( / - / g, '_' ) ;
184
+ switch ( argumentInfo [ key ] . type ) {
185
+ case 'boolean' :
186
+ i += parseBooleanArgument ( key , args , i , parsedArgs . options , value ) ;
187
+ break ;
188
+ case 'number' :
189
+ i += parseNumberArgument ( key , args , i , parsedArgs . options , value ) ;
190
+ break ;
191
+ case 'string' :
192
+ i += parseStringArgument ( key , args , i , parsedArgs . options , value ) ;
193
+ break ;
194
+ case 'json' :
195
+ i += parseJsonArgument ( key , args , i , parsedArgs . options , value ) ;
196
+ break ;
197
+ }
198
+ } else if ( arg . startsWith ( '-' ) ) {
199
+ const shortArg = arg . slice ( 1 ) ;
200
+ for ( let j = 0 ; j < shortArg . length ; j ++ ) {
201
+ const key = shortArgsMap [ shortArg [ j ] ] ;
202
+ if ( ! key ) {
203
+ throw new Error ( `Invalid option: ${ shortArg [ j ] } ` ) ;
204
+ }
205
+ if ( argumentInfo [ key ] . type === 'boolean' ) {
206
+ i += parseBooleanArgument ( key , args , i , parsedArgs . options , null ) ;
207
+ } else if ( j > 0 || shortArg . length > 1 ) {
208
+ throw new Error ( `Invalid option: ${ shortArg } ` ) ;
209
+ } else {
210
+ switch ( argumentInfo [ key ] . type ) {
211
+ case 'number' :
212
+ i += parseNumberArgument ( key , args , i , parsedArgs . options , null ) ;
213
+ break ;
214
+ case 'string' :
215
+ i += parseStringArgument ( key , args , i , parsedArgs . options , null ) ;
216
+ break ;
217
+ case 'json' :
218
+ i += parseJsonArgument ( key , args , i , parsedArgs . options , null ) ;
219
+ break ;
220
+ }
221
+ }
222
+ }
223
+ } else {
224
+ parsedArgs . args . push ( arg ) ;
225
+ }
226
+ }
227
+
228
+ return parsedArgs ;
229
+ }
230
+ } ;
0 commit comments