1 | If you want to write an option parser, and have it be good, there are
|
---|
2 | two ways to do it. The Right Way, and the Wrong Way.
|
---|
3 |
|
---|
4 | The Wrong Way is to sit down and write an option parser. We've all done
|
---|
5 | that.
|
---|
6 |
|
---|
7 | The Right Way is to write some complex configurable program with so many
|
---|
8 | options that you hit the limit of your frustration just trying to
|
---|
9 | manage them all, and defer it with duct-tape solutions until you see
|
---|
10 | exactly to the core of the problem, and finally snap and write an
|
---|
11 | awesome option parser.
|
---|
12 |
|
---|
13 | If you want to write an option parser, don't write an option parser.
|
---|
14 | Write a package manager, or a source control system, or a service
|
---|
15 | restarter, or an operating system. You probably won't end up with a
|
---|
16 | good one of those, but if you don't give up, and you are relentless and
|
---|
17 | diligent enough in your procrastination, you may just end up with a very
|
---|
18 | nice option parser.
|
---|
19 |
|
---|
20 | ## USAGE
|
---|
21 |
|
---|
22 | ```javascript
|
---|
23 | // my-program.js
|
---|
24 | var nopt = require("nopt")
|
---|
25 | , Stream = require("stream").Stream
|
---|
26 | , path = require("path")
|
---|
27 | , knownOpts = { "foo" : [String, null]
|
---|
28 | , "bar" : [Stream, Number]
|
---|
29 | , "baz" : path
|
---|
30 | , "bloo" : [ "big", "medium", "small" ]
|
---|
31 | , "flag" : Boolean
|
---|
32 | , "pick" : Boolean
|
---|
33 | , "many1" : [String, Array]
|
---|
34 | , "many2" : [path, Array]
|
---|
35 | }
|
---|
36 | , shortHands = { "foofoo" : ["--foo", "Mr. Foo"]
|
---|
37 | , "b7" : ["--bar", "7"]
|
---|
38 | , "m" : ["--bloo", "medium"]
|
---|
39 | , "p" : ["--pick"]
|
---|
40 | , "f" : ["--flag"]
|
---|
41 | }
|
---|
42 | // everything is optional.
|
---|
43 | // knownOpts and shorthands default to {}
|
---|
44 | // arg list defaults to process.argv
|
---|
45 | // slice defaults to 2
|
---|
46 | , parsed = nopt(knownOpts, shortHands, process.argv, 2)
|
---|
47 | console.log(parsed)
|
---|
48 | ```
|
---|
49 |
|
---|
50 | This would give you support for any of the following:
|
---|
51 |
|
---|
52 | ```console
|
---|
53 | $ node my-program.js --foo "blerp" --no-flag
|
---|
54 | { "foo" : "blerp", "flag" : false }
|
---|
55 |
|
---|
56 | $ node my-program.js ---bar 7 --foo "Mr. Hand" --flag
|
---|
57 | { bar: 7, foo: "Mr. Hand", flag: true }
|
---|
58 |
|
---|
59 | $ node my-program.js --foo "blerp" -f -----p
|
---|
60 | { foo: "blerp", flag: true, pick: true }
|
---|
61 |
|
---|
62 | $ node my-program.js -fp --foofoo
|
---|
63 | { foo: "Mr. Foo", flag: true, pick: true }
|
---|
64 |
|
---|
65 | $ node my-program.js --foofoo -- -fp # -- stops the flag parsing.
|
---|
66 | { foo: "Mr. Foo", argv: { remain: ["-fp"] } }
|
---|
67 |
|
---|
68 | $ node my-program.js --blatzk -fp # unknown opts are ok.
|
---|
69 | { blatzk: true, flag: true, pick: true }
|
---|
70 |
|
---|
71 | $ node my-program.js --blatzk=1000 -fp # but you need to use = if they have a value
|
---|
72 | { blatzk: 1000, flag: true, pick: true }
|
---|
73 |
|
---|
74 | $ node my-program.js --no-blatzk -fp # unless they start with "no-"
|
---|
75 | { blatzk: false, flag: true, pick: true }
|
---|
76 |
|
---|
77 | $ node my-program.js --baz b/a/z # known paths are resolved.
|
---|
78 | { baz: "/Users/isaacs/b/a/z" }
|
---|
79 |
|
---|
80 | # if Array is one of the types, then it can take many
|
---|
81 | # values, and will always be an array. The other types provided
|
---|
82 | # specify what types are allowed in the list.
|
---|
83 |
|
---|
84 | $ node my-program.js --many1 5 --many1 null --many1 foo
|
---|
85 | { many1: ["5", "null", "foo"] }
|
---|
86 |
|
---|
87 | $ node my-program.js --many2 foo --many2 bar
|
---|
88 | { many2: ["/path/to/foo", "path/to/bar"] }
|
---|
89 | ```
|
---|
90 |
|
---|
91 | Read the tests at the bottom of `lib/nopt.js` for more examples of
|
---|
92 | what this puppy can do.
|
---|
93 |
|
---|
94 | ## Types
|
---|
95 |
|
---|
96 | The following types are supported, and defined on `nopt.typeDefs`
|
---|
97 |
|
---|
98 | * String: A normal string. No parsing is done.
|
---|
99 | * path: A file system path. Gets resolved against cwd if not absolute.
|
---|
100 | * url: A url. If it doesn't parse, it isn't accepted.
|
---|
101 | * Number: Must be numeric.
|
---|
102 | * Date: Must parse as a date. If it does, and `Date` is one of the options,
|
---|
103 | then it will return a Date object, not a string.
|
---|
104 | * Boolean: Must be either `true` or `false`. If an option is a boolean,
|
---|
105 | then it does not need a value, and its presence will imply `true` as
|
---|
106 | the value. To negate boolean flags, do `--no-whatever` or `--whatever
|
---|
107 | false`
|
---|
108 | * NaN: Means that the option is strictly not allowed. Any value will
|
---|
109 | fail.
|
---|
110 | * Stream: An object matching the "Stream" class in node. Valuable
|
---|
111 | for use when validating programmatically. (npm uses this to let you
|
---|
112 | supply any WriteStream on the `outfd` and `logfd` config options.)
|
---|
113 | * Array: If `Array` is specified as one of the types, then the value
|
---|
114 | will be parsed as a list of options. This means that multiple values
|
---|
115 | can be specified, and that the value will always be an array.
|
---|
116 |
|
---|
117 | If a type is an array of values not on this list, then those are
|
---|
118 | considered valid values. For instance, in the example above, the
|
---|
119 | `--bloo` option can only be one of `"big"`, `"medium"`, or `"small"`,
|
---|
120 | and any other value will be rejected.
|
---|
121 |
|
---|
122 | When parsing unknown fields, `"true"`, `"false"`, and `"null"` will be
|
---|
123 | interpreted as their JavaScript equivalents.
|
---|
124 |
|
---|
125 | You can also mix types and values, or multiple types, in a list. For
|
---|
126 | instance `{ blah: [Number, null] }` would allow a value to be set to
|
---|
127 | either a Number or null. When types are ordered, this implies a
|
---|
128 | preference, and the first type that can be used to properly interpret
|
---|
129 | the value will be used.
|
---|
130 |
|
---|
131 | To define a new type, add it to `nopt.typeDefs`. Each item in that
|
---|
132 | hash is an object with a `type` member and a `validate` method. The
|
---|
133 | `type` member is an object that matches what goes in the type list. The
|
---|
134 | `validate` method is a function that gets called with `validate(data,
|
---|
135 | key, val)`. Validate methods should assign `data[key]` to the valid
|
---|
136 | value of `val` if it can be handled properly, or return boolean
|
---|
137 | `false` if it cannot.
|
---|
138 |
|
---|
139 | You can also call `nopt.clean(data, types, typeDefs)` to clean up a
|
---|
140 | config object and remove its invalid properties.
|
---|
141 |
|
---|
142 | ## Error Handling
|
---|
143 |
|
---|
144 | By default, nopt outputs a warning to standard error when invalid values for
|
---|
145 | known options are found. You can change this behavior by assigning a method
|
---|
146 | to `nopt.invalidHandler`. This method will be called with
|
---|
147 | the offending `nopt.invalidHandler(key, val, types)`.
|
---|
148 |
|
---|
149 | If no `nopt.invalidHandler` is assigned, then it will console.error
|
---|
150 | its whining. If it is assigned to boolean `false` then the warning is
|
---|
151 | suppressed.
|
---|
152 |
|
---|
153 | ## Abbreviations
|
---|
154 |
|
---|
155 | Yes, they are supported. If you define options like this:
|
---|
156 |
|
---|
157 | ```javascript
|
---|
158 | { "foolhardyelephants" : Boolean
|
---|
159 | , "pileofmonkeys" : Boolean }
|
---|
160 | ```
|
---|
161 |
|
---|
162 | Then this will work:
|
---|
163 |
|
---|
164 | ```bash
|
---|
165 | node program.js --foolhar --pil
|
---|
166 | node program.js --no-f --pileofmon
|
---|
167 | # etc.
|
---|
168 | ```
|
---|
169 |
|
---|
170 | ## Shorthands
|
---|
171 |
|
---|
172 | Shorthands are a hash of shorter option names to a snippet of args that
|
---|
173 | they expand to.
|
---|
174 |
|
---|
175 | If multiple one-character shorthands are all combined, and the
|
---|
176 | combination does not unambiguously match any other option or shorthand,
|
---|
177 | then they will be broken up into their constituent parts. For example:
|
---|
178 |
|
---|
179 | ```json
|
---|
180 | { "s" : ["--loglevel", "silent"]
|
---|
181 | , "g" : "--global"
|
---|
182 | , "f" : "--force"
|
---|
183 | , "p" : "--parseable"
|
---|
184 | , "l" : "--long"
|
---|
185 | }
|
---|
186 | ```
|
---|
187 |
|
---|
188 | ```bash
|
---|
189 | npm ls -sgflp
|
---|
190 | # just like doing this:
|
---|
191 | npm ls --loglevel silent --global --force --long --parseable
|
---|
192 | ```
|
---|
193 |
|
---|
194 | ## The Rest of the args
|
---|
195 |
|
---|
196 | The config object returned by nopt is given a special member called
|
---|
197 | `argv`, which is an object with the following fields:
|
---|
198 |
|
---|
199 | * `remain`: The remaining args after all the parsing has occurred.
|
---|
200 | * `original`: The args as they originally appeared.
|
---|
201 | * `cooked`: The args after flags and shorthands are expanded.
|
---|
202 |
|
---|
203 | ## Slicing
|
---|
204 |
|
---|
205 | Node programs are called with more or less the exact argv as it appears
|
---|
206 | in C land, after the v8 and node-specific options have been plucked off.
|
---|
207 | As such, `argv[0]` is always `node` and `argv[1]` is always the
|
---|
208 | JavaScript program being run.
|
---|
209 |
|
---|
210 | That's usually not very useful to you. So they're sliced off by
|
---|
211 | default. If you want them, then you can pass in `0` as the last
|
---|
212 | argument, or any other number that you'd like to slice off the start of
|
---|
213 | the list.
|
---|