1 | Capitano
|
2 | ========
|
3 |
|
4 | [![npm version](https://badge.fury.io/js/capitano.svg)](http://badge.fury.io/js/capitano)
|
5 | [![dependencies](https://david-dm.org/resin-io/capitano.png)](https://david-dm.org/username/repo.png)
|
6 | [![Build Status](https://travis-ci.org/resin-io/capitano.svg?branch=master)](https://travis-ci.org/resin-io/capitano)
|
7 |
|
8 | Capitano allows you to craft powerful command line applications, your way.
|
9 |
|
10 | ```coffee
|
11 | capitano = require('capitano')
|
12 |
|
13 | capitano.permission 'jviotti', (done) ->
|
14 | return done() if process.env.USER is 'jviotti'
|
15 | done(new Error('You are not jviotti!'))
|
16 |
|
17 | capitano.command
|
18 | signature: 'utils print <title> [words...]'
|
19 | options: [
|
20 | signature: 'date'
|
21 | boolean: true
|
22 | alias: [ 'd' ]
|
23 | ]
|
24 | permission: 'jviotti'
|
25 | action: (params, options) ->
|
26 | log = ''
|
27 |
|
28 | if options.date
|
29 | log += "#{new Date()} "
|
30 |
|
31 | log += "#{params.title}: #{params.words}"
|
32 |
|
33 | console.log(log)
|
34 |
|
35 | capitano.run process.argv, (error) ->
|
36 | throw error if error?
|
37 | ```
|
38 |
|
39 | ***
|
40 |
|
41 | ```sh
|
42 | $ myCoolApp utils print Error Something very bad happened
|
43 | Error: Something very bad happened
|
44 |
|
45 | $ myCoolApp utils print Error Something very bad happened -d
|
46 | Thu Dec 18 2014 14:49:27 GMT-0400 (BOT) Error: Something very bad happened
|
47 | ```
|
48 |
|
49 | We wrote Capitano as we couldn't find any NodeJS command line parser that met our needs. Alternatives such as [commander.js](https://github.com/tj/commander.js), albeit being very good, didn't have good support for features such as infinitely nested git-like subcommands, per-command options and complete customisation. Capitano is our attempt at creating a non opitionated command line parser that can do everything you can imagine.
|
50 |
|
51 | Features
|
52 | --------
|
53 |
|
54 | - Infinitely nested git-like subcommands.
|
55 | - Global and per-command options.
|
56 | - Variadic arguments.
|
57 | - Option aliases.
|
58 | - Separate between parsing and executing command line arguments.
|
59 | - No built-in generated help page, you roll your own.
|
60 | - No built-in commands, you have a high degree of control over your app.
|
61 | - Permission support.
|
62 |
|
63 | Installation
|
64 | ------------
|
65 |
|
66 | Install `capitano` by running:
|
67 |
|
68 | ```sh
|
69 | $ npm install --save capitano
|
70 | ```
|
71 |
|
72 | API
|
73 | -------
|
74 |
|
75 | ## capitano.command(options)
|
76 |
|
77 | Register a command. Capitano understands the following options, but you can pass custom options to do nifty things (see the [examples section](https://github.com/resin-io/capitano#examples)):
|
78 |
|
79 | ### signature (string)
|
80 |
|
81 | The command signature. If it's `*`, it will match anything that is not matched by other commands.
|
82 |
|
83 | ### action (function)
|
84 |
|
85 | Function to call when the signature is matched. This function gets passed a parameter object, an options object and a `callback` to be called when the action is finished.
|
86 |
|
87 | If the `callback` argument is not declared in the action function, it'll be called implicitly, however in order to ensure Capitano works as expected, call the `callback` function if your action is async.
|
88 |
|
89 | ### options ([object])
|
90 |
|
91 | Array of objects describing the options specific to this command. See the [options section](https://github.com/resin-io/capitano#option) for more information.
|
92 |
|
93 | ### permission (string)
|
94 |
|
95 | Require a certain previously registered permission by name. If the permission requirements are not met, the command action will not be called and `capitano.execute()`, or `capitano.run()` will get the error you passed in from the permission function in their callbacks.
|
96 |
|
97 | Notice that Capitano doesn't currently supports passing an array of permissions. If you have that specific use case, you'll have to create a new permission that combines the other ones.
|
98 |
|
99 | ## capitano.globalOption(options)
|
100 |
|
101 | Register a global option, which will be accessible from every command (and from outside too!) so be careful about naming collisions!
|
102 |
|
103 | It accepts an object containing the following options:
|
104 |
|
105 | ### signature (string)
|
106 |
|
107 | The option signature **excluding** any parameter (`foo` instead of `foo <bar>`).
|
108 |
|
109 | ### boolean (boolean)
|
110 |
|
111 | Whether the option is boolean (doesn't accepts any parameters). It defaults to `false`. If `parameter` is defined, then `boolean` should be `false`.
|
112 |
|
113 | ### parameter (string)
|
114 |
|
115 | The name of the parameter, excluding required/optional tags (`bar` instead of `<bar>`). Notice that if you set `boolean: true`, then you have to omit this option.
|
116 |
|
117 | ### alias (string|[string])
|
118 |
|
119 | Define an alias, or a set of alias for an option. Aliases can contain single letter abbreviations (`f`, `l`) or full option names (`baz`, `foo`).
|
120 |
|
121 | ## capitano.permission(name, function)
|
122 |
|
123 | It registers a permission function under a certain name. The permission function is passed a `done()` function that accepts an error instance in case the user doesn't fits the permission requirements. Pass nothing if the permission requirement was matched.
|
124 |
|
125 | **Note:** You must call the `done()` function, even if your permission function is synchronous, in order for Capitano to continue.
|
126 |
|
127 | ## capitano.run(argv, callback)
|
128 |
|
129 | Run and execute the application given a set of arguments (usually `process.argv`):
|
130 |
|
131 | ```coffee
|
132 | capitano.run(process.argv)
|
133 | ```
|
134 |
|
135 | **Note:** `capitano.run` is a shorcut function for `capitano.execute(capitano.parse(argv), callback)`. You will usually use this function, however you can use `parse()` and `execute()` in particular situations when you need to differenciate between parsing and executing the commands.
|
136 |
|
137 | ## capitano.parse(argv)
|
138 |
|
139 | Parse, but not execute the command line arguments (usually `process.argv`).
|
140 |
|
141 | It returns a `cli` object containing three fields:
|
142 |
|
143 | ### command (string)
|
144 |
|
145 | A string representing the issued command, omitting any option.
|
146 |
|
147 | ### options (object)
|
148 |
|
149 | An object containing the raw representation of the given options.
|
150 |
|
151 | ### globals (object)
|
152 |
|
153 | An object containing the matches and parsed global options.
|
154 |
|
155 | ## capitano.execute(cli, callback)
|
156 |
|
157 | It accepts a `cli` object (returned by [capitano.parse()](https://github.com/resin-io/capitano#capitanoparseargv)) and
|
158 | executes the corresponding command, with it's corresponding options.
|
159 |
|
160 | You're expected to provide your own error handling mechanism here, or in the `capitano.run` if executing from there.
|
161 |
|
162 | ## capitano.state
|
163 |
|
164 | An object containing the current registered commands an options. As with Capitano you're expected to implement every command (including `help`, etc) this object comes handy to accomplish a wide range of tasks.
|
165 |
|
166 | It includes the following fields:
|
167 |
|
168 | ### commands (array)
|
169 |
|
170 | An array containing every registered command so far (with `capitano.command()`)
|
171 |
|
172 | See the [Command class](https://github.com/resin-io/capitano#command) for more information.
|
173 |
|
174 | ### globalOptions (array)
|
175 |
|
176 | An array containing every registered global option so far (with `capitano.globalOption()`).
|
177 |
|
178 | See the [Option class](https://github.com/resin-io/capitano#option) for more information.
|
179 |
|
180 | ### findCommandBySignature(signature)
|
181 |
|
182 | A self explanatory function that returns a command that matches a specific signature.
|
183 |
|
184 | ### getMatchCommand(signature)
|
185 |
|
186 | Get command that matches a signature, without taking parameters into account.
|
187 |
|
188 | This means that a command `app create <id>` will be matched by a signature `app create`.
|
189 |
|
190 | ## capitano.defaults
|
191 |
|
192 | An object containing some settings used by Capitano.
|
193 |
|
194 | It includes the following fields:
|
195 |
|
196 | - `signatures.wildcard (string)` The wildcard symbol. Defaults to `*`.
|
197 | - `actions.commandNotFound(signature)` The function called when a command was not found. By default, it prints a boring `Command not found: <signature>` and exits with an error code 1.
|
198 |
|
199 | **Pro tip:** If you want to modify these settings, do it as early as possible (before registering any commands/global options) as some settings are used when performing the mentioned tasks.
|
200 |
|
201 | Classes
|
202 | -------
|
203 |
|
204 | ### Command
|
205 |
|
206 | The Capitano Command class contains the following public fields:
|
207 |
|
208 | #### Command#signature (Signature)
|
209 |
|
210 | See the [Signature class](https://github.com/resin-io/capitano#signature).
|
211 |
|
212 | #### Command#options ([Option])
|
213 |
|
214 | An array of [Option classes](https://github.com/resin-io/capitano#option).
|
215 |
|
216 | #### Command#isWildcard()
|
217 |
|
218 | A predicate method that returns `true` if a command represents a wildcard.
|
219 |
|
220 | ***
|
221 |
|
222 | ### Signature
|
223 |
|
224 | The Capitano Signature class contains the following public fields:
|
225 |
|
226 | #### Signature#hasParameters()
|
227 |
|
228 | A predicate method that returns `true` if the signature has at least one parameter.
|
229 |
|
230 | #### Signature#hasVariadicParameters()
|
231 |
|
232 | A predicate method that returns `true` if the signature has at least one variadic parameter.
|
233 |
|
234 | #### Signature#isWildcard()
|
235 |
|
236 | A predicate method that returns `true` if the signature represents a wildcard.
|
237 |
|
238 | ***
|
239 |
|
240 | ### Option
|
241 |
|
242 | The Capitano Option class contains the following public fields:
|
243 |
|
244 | #### Option#signature
|
245 |
|
246 | See [Signature class](https://github.com/resin-io/capitano#signature).
|
247 |
|
248 | #### Option#alias (string|[string])
|
249 |
|
250 | A string or array of string alias.
|
251 |
|
252 | #### Option#boolean (boolean)
|
253 |
|
254 | Whether the option is boolean or not.
|
255 |
|
256 | #### Option#parameter (string)
|
257 |
|
258 | An option parameter (optional).
|
259 |
|
260 | #### Option#required (boolean|string)
|
261 |
|
262 | Defines whether an option is required. If the field is `true`, a generic error is thrown, otherwise you can set a custom error message by setting to a `string`.
|
263 |
|
264 | Examples
|
265 | --------
|
266 |
|
267 | Capitano is very flexible, allowing you to implement all sort of crazy stuff. Here I present some common patterns that I've been doing on Capitano. If you have an interesting idea that you think it's worth to share, please submit a PR!
|
268 |
|
269 | ### Generated help
|
270 |
|
271 | Notice this is a very rudimentary help page and lacks features such as printing global options, command specific options, handling correct aligment, etc, but you can at least get an idea on how to implement this for yourself.
|
272 |
|
273 | ```coffee
|
274 | capitano = require('capitano')
|
275 |
|
276 | capitano.command
|
277 | signature: 'version'
|
278 | description: 'output version information'
|
279 | action: ...
|
280 |
|
281 | capitano.command
|
282 | signature: 'help'
|
283 | description: 'output general help page'
|
284 | action: ->
|
285 | console.log("Usage: #{myAppName} [COMMANDS] [OPTIONS]")
|
286 | console.log('\nCommands:\n')
|
287 |
|
288 | for command in capitano.state.commands
|
289 | continue if command.isWildcard()
|
290 | console.log("\t#{command.signature}\t\t\t"#{command.description})
|
291 |
|
292 | capitano.run process.argv, (error) ->
|
293 | throw error if error?
|
294 | ```
|
295 |
|
296 | ***
|
297 |
|
298 | ```sh
|
299 | $ app help
|
300 | Usage: MyCoolApp [COMMANDS] [OPTIONS]
|
301 |
|
302 | Commands:
|
303 |
|
304 | version output version information
|
305 | help output general help page
|
306 | ```
|
307 |
|
308 | ### Command specific help pages
|
309 |
|
310 | ```coffee
|
311 | capitano = require('capitano')
|
312 |
|
313 | capitano.command
|
314 | signature: 'version'
|
315 | description: 'output version information'
|
316 | help: '''
|
317 | Software versioning is the process of assigning either unique version names or unique version numbers to unique states of computer software. Within a given version number category (major, minor), these numbers are generally assigned in increasing order and correspond to new developments in the software. At a fine-grained level, revision control is often used for keeping track of incrementally different versions of electronic information, whether or not this information is computer software.
|
318 | '''
|
319 | action: ...
|
320 |
|
321 | capitano.command
|
322 | signature: 'help [command...]'
|
323 | description: 'output general help page'
|
324 | action: (params) ->
|
325 | return outputGeneralHelp() if not params?
|
326 |
|
327 | command = capitano.state.getMatchCommand(params.command)
|
328 | if not command? or command.isWildcard()
|
329 | return capitano.defaults.actions.commandNotFound(params.command)
|
330 |
|
331 | console.log(command.help)
|
332 |
|
333 | capitano.run process.argv, (error) ->
|
334 | throw error if error?
|
335 | ```
|
336 |
|
337 | ***
|
338 |
|
339 | ```sh
|
340 | $ app help version
|
341 | Software versioning is the process of assigning either unique version names or unique version numbers to unique states of computer software. Within a given version number category (major, minor), these numbers are generally assigned in increasing order and correspond to new developments in the software. At a fine-grained level, revision control is often used for keeping track of incrementally different versions of electronic information, whether or not this information is computer software.
|
342 | ```
|
343 |
|
344 | Tests
|
345 | -----
|
346 |
|
347 | Run the test suite by doing:
|
348 |
|
349 | ```sh
|
350 | $ gulp test
|
351 | ```
|
352 |
|
353 | ChangeLog
|
354 | ---------
|
355 |
|
356 | ### 1.3.1
|
357 |
|
358 | - Catch action errors and send them to the callback automatically. See [#17](https://github.com/resin-io/capitano/pull/17).
|
359 |
|
360 | ### 1.3.0
|
361 |
|
362 | - Implement permission support. Discussed in [#15](https://github.com/resin-io/capitano/issues/15).
|
363 |
|
364 | ### 1.2.0
|
365 |
|
366 | - Implement action callback support. Discussed in [#11](https://github.com/resin-io/capitano/issues/11).
|
367 |
|
368 | ### 1.1.1
|
369 |
|
370 | - Fix issue with parsing words that start with a number.
|
371 |
|
372 | ### 1.1.0
|
373 |
|
374 | - Implement support for required options.
|
375 |
|
376 | ### 1.0.4
|
377 |
|
378 | - Fix issue when instantiating Parameter instances with a number directly.
|
379 |
|
380 | ### 1.0.3
|
381 |
|
382 | - Fix issue with quotation of multi word parameters. Thanks [@Page-](https://github.com/Page-)!
|
383 |
|
384 | ### 1.0.2
|
385 |
|
386 | - Fix https://github.com/resin-io/capitano/issues/4.
|
387 |
|
388 | ### 1.0.1
|
389 |
|
390 | - Fix issues with path and quoted multi string parameters.
|
391 |
|
392 | Contribute
|
393 | ----------
|
394 |
|
395 | - Issue Tracker: [github.com/resin-io/capitano/issues](https://github.com/resin-io/capitano/issues)
|
396 | - Source Code: [github.com/resin-io/capitano](https://github.com/resin-io/capitano)
|
397 |
|
398 | Before submitting a PR, please make sure that you include tests, and that [coffeelint](http://www.coffeelint.org/) runs without any warning:
|
399 |
|
400 | ```sh
|
401 | $ gulp lint
|
402 | ```
|
403 |
|
404 | Support
|
405 | -------
|
406 |
|
407 | If you're having any problem, please [raise an issue](https://github.com/resin-io/capitano/issues) on GitHub.
|
408 |
|
409 | TODO
|
410 | -------
|
411 |
|
412 | - Options default values.
|
413 | - Allow comma-separated parameter values (numbers, floats, etc).
|
414 |
|
415 | License
|
416 | -------
|
417 |
|
418 | The project is licensed under the MIT license.
|