Skip to content

Commit 84449de

Browse files
committed
Added support for parsing quoted parameters.
1 parent 67d1a20 commit 84449de

File tree

5 files changed

+92
-49
lines changed

5 files changed

+92
-49
lines changed

README.md

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ After writing countless CLI utilities, it became clear that the majority of most
1212
> **This tool is just a parser.** It parses arguments and optionally enforces developer-defined rules. It exposes all relevant aspects of the arguments so developers can use the parsed content in any manner. It does not attempt to autogenerate help screens or apply any other "blackbox" functionality. WYSIWYG.<br/>
1313
**If your tool needs more management/organization features, see the [@author.io/shell](https://github.com/author/shell) micro-framework** _(which is built atop this library)_**.**
1414

15-
## Example
15+
## Verbose Example
1616

1717
**Install:** `npm install @author.io/arg`
1818

@@ -73,7 +73,7 @@ _Output:_
7373
}
7474
```
7575

76-
## Even Simpler Syntax
76+
## Simpler Syntax
7777

7878
For brevity, there is also a `configure` method which will automatically do all of the things the first script does, but with minimal code.
7979

@@ -125,35 +125,15 @@ console.log(Args.data)
125125

126126
## API/Usage
127127

128-
The source code is pretty easy to figure out, but here's what's possible:
128+
The source code is pretty easy to figure out, but here's an overview:
129129

130-
### Properties
130+
## Configuring Parser Logic
131131

132-
- `flags`: An array of the unique flag names passed to the application.
133-
- `data`: A key/value object representing all data passed to the application. If a flag is passed in more than once and duplicates are _not_ suppressed, the value will be an array.
134-
- `length` The total number of arguments passed to the application.
135-
- `valid` A boolean representing whether all of the validation rules passed or not.
136-
- `violations` An array of violations (this is an empty array when everything is valid).
137-
138-
## Configuration Methods
139-
140-
The main methods are used to configure rules.
141-
142-
The following attributes configuration attributes can be set for each flag:
143-
144-
- `required` - Indicates the flag must be present in the command.
145-
- `default` - A value to use when the flag is not specified.
146-
- `type` - The data type. Supports primitives like `Boolean` or their text (typeof) equivalent (i.e. "`boolean`").
147-
- `alias` - A string representing an alternative name for the flag.
148-
- `aliases` - Support for multiple aliases.
149-
- `allowMultipleValues` - If a flag is specified more than once, capture all values (instead of only the last one specified).
150-
- `options` - An array of valid values for the flag.
151-
152-
### configure({...})
132+
There are two ways to configure the parser. A single `configure()` method can describe everything, or individual methods can be used to dynamically define the parsing logic.
153133

154-
This method accepts a configuration, which will automatically invoke all of the appropriate configuration methods using a shorthand syntax.
134+
### Using `configure()`
155135

156-
For example:
136+
The `configure()` method accepts a shorthand (yet-easily-understood) configuration object.
157137

158138
```javascript
159139
Args.configure({
@@ -170,37 +150,51 @@ Args.configure({
170150
})
171151
```
172152

173-
### require('flag1', 'flag2', ...)
153+
_Purpose:_
154+
155+
- `required` - Indicates the flag must be present in the command.
156+
- `default` - A value to use when the flag is not specified.
157+
- `type` - The data type. Supports primitives like `Boolean` or their text (typeof) equivalent (i.e. "`boolean`").
158+
- `alias` - A string representing an alternative name for the flag.
159+
- `aliases` - Support for multiple aliases.
160+
- `allowMultipleValues` - If a flag is specified more than once, capture all values (instead of only the last one specified).
161+
- `options` - An array of valid values for the flag.
162+
163+
### Using Individual Methods
164+
165+
The following methods can be used to dynamically construct the parsing logic, or modify existing logic.
166+
167+
#### require('flag1', 'flag2', ...)
174168

175169
Require the presence of specific flags amongst the arguments. Automatically executes `recognize` for all required flags.
176170

177-
### recognize('flag1', 'flag2', ...)
171+
#### recognize('flag1', 'flag2', ...)
178172

179173
Register "known" flags. This is useful when you want to prevent unrecognized flags from being passed to the application.
180174

181-
### types({...})
175+
#### types({...})
182176

183177
Identify the data type of a flag or series of flags. Automatically executes `recognize` for any flags specified amongst the data types.
184178

185-
### defaults({...})
179+
#### defaults({...})
186180

187181
Identify default values for flags.
188182

189183
Automatically executes `recognize` for any flags specified amongst the defaults.
190184

191-
### alias({...})
185+
#### alias({...})
192186

193187
Identify aliases for recognized flags.
194188

195189
Automatically executes `recognize` for any flags specified amongst the defaults.
196190

197-
### allowMultipleValues('flag1', 'flag2', ...)
191+
#### allowMultipleValues('flag1', 'flag2', ...)
198192

199193
By default, if the same flag is defined multiple times, only the last value is recognized. Setting `allowMultiple` on a flag will capture all values (as an array).
200194

201195
Automatically executes `recognize` for any flags specified amongst the defaults.
202196

203-
### setOptions('flag', 'optionA', 'optionB')
197+
#### setOptions('flag', 'optionA', 'optionB')
204198

205199
A list/enumeration of values will be enforced _if_ the flag is set. If a flag contains a value not present in the list, a violation will be recognized.
206200

@@ -212,49 +206,71 @@ Automatically executes `recognize` for any flags specified amongst the defaults.
212206

213207
Enforcement methods are designed to help toggle rules on/off as needed.
214208

215-
There is no special method to enforce a flag value to be within a list of valid options, because this is enforced automatically.
209+
There is no special method to enforce a flag value to be within a list of valid options (enumerability), _because this is enforced automatically_.
216210

217-
### disallowUnrecognized()
211+
#### disallowUnrecognized()
218212

219213
Sets a rule to prevent the presence of any unrecognized flags.
220214

221-
### allowUnrecognized()
215+
#### allowUnrecognized()
222216

223217
Sets a rule to allow the presence of unrecognized flags (this is the default behavior).
224218

225-
### ignoreDataTypes()
219+
#### ignoreDataTypes()
226220

227221
This will ignore data type checks, even if the `types` method has been used to enforce data types.
228222

229-
### enforceDataTypes()
223+
#### enforceDataTypes()
230224

231225
This will enforce data type checks. This is the default behavior.
232226

227+
---
228+
233229
## Helper Methods
234230

235231
The following helper methods are made available for developers who need quick access to flags and enforcement functionality.
236232

237-
### enforceRules()
233+
#### enforceRules()
238234

239235
This method can be used within a process to validate flags and exit with error when validation fails.
240236

241-
### value(flagname)
237+
#### value(flagname)
242238

243239
Retrieve the value of a flag. This accepts flags or aliases. If the specified flag does not exist, a value of `undefined` is returned.
244240

245-
### exists(flagname)
241+
#### exists(flagname)
246242

247243
Returns a boolean value indicating the flag exists.
248244

249245
---
250-
## Metadata
246+
## Defining Metadata
251247

252248
The following methods are made available to manage metadata about flags.
253249

254-
### describe(flagname, description)
250+
#### describe(flagname, description)
255251

256-
Use this message to store a description of the flag. This is stored as metadata. This will throw an error if the flag does not exist.
252+
Use this message to store a description of the flag. This will throw an error if the flag does not exist.
257253

258-
### description(flagname)
254+
#### description(flagname)
259255

260256
Retrieve the description of a flag. Returns `null` if no description is found.
257+
258+
---
259+
260+
## Parser Properties
261+
262+
These are readable properties of the parser. For example:
263+
264+
```javascript
265+
import Args from '@author.io/arg'
266+
267+
Args.configure({...})
268+
269+
console.log(Args.flags, Args.data, ...)
270+
```
271+
272+
- `flags`: An array of the unique flag names passed to the application.
273+
- `data`: A key/value object representing all data passed to the application. If a flag is passed in more than once and duplicates are _not_ suppressed, the value will be an array.
274+
- `length` The total number of arguments passed to the application.
275+
- `valid` A boolean representing whether all of the validation rules passed or not.
276+
- `violations` An array of violations (this is an empty array when everything is valid).

index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import Flag from "./flag.js"
22

3+
const PARSER = /\s*(?:((?:(?:"(?:\\.|[^"])*")|(?:'[^']*')|(?:\\.)|\S)+)\s*)/gi
4+
35
class Parser {
46
#args = []
57
#flags = {}
@@ -134,9 +136,19 @@ class Parser {
134136
return
135137
}
136138

139+
// Normalize the input
140+
// If an array is provided, assume the input has been split into
141+
// argumnets. Otherwise use the parser RegEx pattern to split
142+
// into arguments.
143+
this.#args = Array.isArray(input) ? input : input.match(PARSER).map(i => {
144+
i = i.trim()
145+
const match = i.match(/((^"(.*)"$)|(^'(.*)'$))/i)
146+
return match !== null ? match[3] : i
147+
})
148+
137149
let skipNext = false
138150
let skipped = []
139-
this.#args = (Array.isArray(input) ? input : [input])
151+
140152
this.#args.forEach((arg, i, args) => {
141153
if (!skipNext || arg.startsWith('-')) {
142154
if (arg.startsWith('-')) {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@author.io/arg",
3-
"version": "1.2.5",
3+
"version": "1.2.6",
44
"description": "An argument parser for CLI applications.",
55
"main": "index.js",
66
"scripts": {

test/01-sanity.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,18 @@ test('Basic Tests', t => {
88

99
t.end()
1010
})
11+
12+
test('Escaped parameters', t => {
13+
const Args = new Parser('-r -hdr "CF-IPCountry=US" -kv "test=something" -kv "a=test" demo.js true')
14+
const data = Args.data
15+
16+
t.ok(
17+
data.r === true &&
18+
data.hdr === 'CF-IPCountry=US' &&
19+
data['demo.js'] === true &&
20+
data.true === true
21+
, 'Parsed complex input'
22+
)
23+
24+
t.end()
25+
})

0 commit comments

Comments
 (0)