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