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