UNPKG

16.1 kBMarkdownView Raw
1Capitano
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[![Build status](https://ci.appveyor.com/api/projects/status/fatwdf74xeh8j9ee?svg=true)](https://ci.appveyor.com/project/jviotti/capitano)
8
9Join our online chat at [![Gitter chat](https://badges.gitter.im/resin-io/chat.png)](https://gitter.im/resin-io/chat)
10
11Capitano allows you to craft powerful command line applications, your way.
12
13```coffee
14capitano = require('capitano')
15
16capitano.permission 'jviotti', (done) ->
17 return done() if process.env.USER is 'jviotti'
18 done(new Error('You are not jviotti!'))
19
20capitano.command
21 signature: 'utils print <title> [words...]'
22 options: [
23 signature: 'date'
24 boolean: true
25 alias: [ 'd' ]
26 ]
27 permission: 'jviotti'
28 action: (params, options) ->
29 log = ''
30
31 if options.date
32 log += "#{new Date()} "
33
34 log += "#{params.title}: #{params.words}"
35
36 console.log(log)
37
38capitano.run process.argv, (error) ->
39 throw error if error?
40```
41
42***
43
44```sh
45$ myCoolApp utils print Error Something very bad happened
46Error: Something very bad happened
47
48$ myCoolApp utils print Error Something very bad happened -d
49Thu Dec 18 2014 14:49:27 GMT-0400 (BOT) Error: Something very bad happened
50```
51
52We 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.
53
54Features
55--------
56
57- Infinitely nested git-like subcommands.
58- Global and per-command options.
59- Variadic arguments.
60- Option aliases.
61- Stdin support out of the box.
62- Separate between parsing and executing command line arguments.
63- No built-in generated help page, you roll your own.
64- No built-in commands, you have a high degree of control over your app.
65- Permission support.
66
67Installation
68------------
69
70Install `capitano` by running:
71
72```sh
73$ npm install --save capitano
74```
75
76API
77-------
78
79## capitano.command(options)
80
81Register 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)):
82
83### signature (string)
84
85The command signature. If it's `*`, it will match anything that is not matched by other commands.
86
87You can represent a command that allows input from stdin with the following syntax:
88
89- Required: `<|foo>`.
90- Optional: `[|foo]`.
91
92Notice that you can only have **one** stdin command per signature, and it has to be the **last parameter** of the signature. See the [examples section](https://github.com/resin-io/capitano#examples) for a stdin example.
93
94### action (function)
95
96Function 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.
97
98If 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.
99
100### options ([object])
101
102Array of objects describing the options specific to this command. See the [options section](https://github.com/resin-io/capitano#option) for more information.
103
104### permission (string)
105
106Require 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.
107
108Notice 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.
109
110### root (boolean)
111
112If you specify this option to true, then the command will only be runnable by a user with admin privileges, or prefixed by `sudo`.
113
114## capitano.globalOption(options)
115
116Register a global option, which will be accessible from every command (and from outside too!) so be careful about naming collisions!
117
118It accepts an object containing the following options:
119
120### signature (string)
121
122The option signature **excluding** any parameter (`foo` instead of `foo <bar>`).
123
124### boolean (boolean)
125
126Whether the option is boolean (doesn't accepts any parameters). It defaults to `false`. If `parameter` is defined, then `boolean` should be `false`.
127
128### parameter (string)
129
130The 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.
131
132### alias (string|[string])
133
134Define an alias, or a set of alias for an option. Aliases can contain single letter abbreviations (`f`, `l`) or full option names (`baz`, `foo`).
135
136## capitano.permission(name, function)
137
138It 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.
139
140**Note:** You must call the `done()` function, even if your permission function is synchronous, in order for Capitano to continue.
141
142## capitano.run(argv, callback)
143
144Run and execute the application given a set of arguments (usually `process.argv`):
145
146```coffee
147capitano.run(process.argv)
148```
149
150This is also the command you need to use if you need to call a command from another command. For example:
151
152```coffee
153capitano.run('my other command --foo bar', callback)
154```
155
156**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.
157
158## capitano.parse(argv)
159
160Parse, but not execute the command line arguments (usually `process.argv`).
161
162It returns a `cli` object containing three fields:
163
164### command (string)
165
166A string representing the issued command, omitting any option.
167
168### options (object)
169
170An object containing the raw representation of the given options.
171
172### globals (object)
173
174An object containing the matches and parsed global options.
175
176## capitano.execute(cli, callback)
177
178It accepts a `cli` object (returned by [capitano.parse()](https://github.com/resin-io/capitano#capitanoparseargv)) and
179executes the corresponding command, with it's corresponding options.
180
181You're expected to provide your own error handling mechanism here, or in the `capitano.run` if executing from there.
182
183## capitano.state
184
185An 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.
186
187It includes the following fields:
188
189### commands (array)
190
191An array containing every registered command so far (with `capitano.command()`)
192
193See the [Command class](https://github.com/resin-io/capitano#command) for more information.
194
195### globalOptions (array)
196
197An array containing every registered global option so far (with `capitano.globalOption()`).
198
199See the [Option class](https://github.com/resin-io/capitano#option) for more information.
200
201### findCommandBySignature(signature)
202
203A self explanatory function that returns a command that matches a specific signature.
204
205### getMatchCommand(signature, callback)
206
207Get command that matches a signature, without taking parameters into account.
208
209This means that a command `app create <id>` will be matched by a signature `app create`.
210
211## capitano.defaults
212
213An object containing some settings used by Capitano.
214
215It includes the following fields:
216
217- `signatures.wildcard (string)` The wildcard symbol. Defaults to `*`.
218- `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.
219
220**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.
221
222## capitano.utils
223
224A collection of handy utilities for working with Capitano.
225
226### capitano.utils.getStdin(callback)
227
228Read from stdin. This function is used when you specify a stdin parameter, such as `<|foo>`.
229
230You'll most often use the stdin parameter syntax, but this function is publicly available in case you need more control.
231
232Example:
233
234```coffee
235capitano = require('capitano')
236
237capitano.utils.getStdin (data) ->
238 console.log("We got #{data} from stdin")
239```
240
241Classes
242-------
243
244### Command
245
246The Capitano Command class contains the following public fields:
247
248#### Command#signature (Signature)
249
250See the [Signature class](https://github.com/resin-io/capitano#signature).
251
252#### Command#options ([Option])
253
254An array of [Option classes](https://github.com/resin-io/capitano#option).
255
256#### Command#isWildcard()
257
258A predicate method that returns `true` if a command represents a wildcard.
259
260***
261
262### Signature
263
264The Capitano Signature class contains the following public fields:
265
266#### Signature#hasParameters()
267
268A predicate method that returns `true` if the signature has at least one parameter.
269
270#### Signature#hasVariadicParameters()
271
272A predicate method that returns `true` if the signature has at least one variadic parameter.
273
274#### Signature#isWildcard()
275
276A predicate method that returns `true` if the signature represents a wildcard.
277
278#### Signature#allowsStdin()
279
280A predicate method that returns `true` if the signature has at least one stdin parameter.
281
282***
283
284### Option
285
286The Capitano Option class contains the following public fields:
287
288#### Option#signature
289
290See [Signature class](https://github.com/resin-io/capitano#signature).
291
292#### Option#alias (string|[string])
293
294A string or array of string alias.
295
296#### Option#boolean (boolean)
297
298Whether the option is boolean or not.
299
300#### Option#parameter (string)
301
302An option parameter (optional).
303
304#### Option#required (boolean|string)
305
306Defines 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`.
307
308Examples
309--------
310
311Capitano 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!
312
313### Stdin input
314
315```coffee
316capitano = require('capitano')
317
318capitano.command
319 signature: 'foo <|bar>'
320 description: 'a command that accepts stdin input'
321 action: (params, options, done) ->
322 console.log("The input is: #{params.bar}")
323 done()
324
325capitano.run process.argv, (error) ->
326 throw error if error?
327```
328
329***
330
331```sh
332$ echo "Hello World" | stdinApp foo
333The input is: Hello World
334
335# Old way still works
336
337$ stdinApp foo "Hello World"
338The input is: Hello World
339```
340
341### Generated help
342
343Notice 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.
344
345```coffee
346capitano = require('capitano')
347
348capitano.command
349 signature: 'version'
350 description: 'output version information'
351 action: ...
352
353capitano.command
354 signature: 'help'
355 description: 'output general help page'
356 action: ->
357 console.log("Usage: #{myAppName} [COMMANDS] [OPTIONS]")
358 console.log('\nCommands:\n')
359
360 for command in capitano.state.commands
361 continue if command.isWildcard()
362 console.log("\t#{command.signature}\t\t\t"#{command.description})
363
364capitano.run process.argv, (error) ->
365 throw error if error?
366```
367
368***
369
370```sh
371$ app help
372Usage: MyCoolApp [COMMANDS] [OPTIONS]
373
374Commands:
375
376 version output version information
377 help output general help page
378```
379
380### Command specific help pages
381
382```coffee
383capitano = require('capitano')
384
385capitano.command
386 signature: 'version'
387 description: 'output version information'
388 help: '''
389 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.
390 '''
391 action: ...
392
393capitano.command
394 signature: 'help [command...]'
395 description: 'output general help page'
396 action: (params, options, done) ->
397 return outputGeneralHelp() if not params?
398
399 capitano.state.getMatchCommand params.command, (error, command) ->
400 return done(error) if error?
401
402 if not command? or command.isWildcard()
403 return capitano.defaults.actions.commandNotFound(params.command)
404
405 console.log(command.help)
406 done()
407
408capitano.run process.argv, (error) ->
409 throw error if error?
410```
411
412***
413
414```sh
415$ app help version
416Software 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.
417```
418
419Tests
420-----
421
422Run the test suite by doing:
423
424```sh
425$ gulp test
426```
427
428ChangeLog
429---------
430
431### 1.7.1
432
433- Fix required options errors being thrown synchronously.
434
435### 1.7.0
436
437- Add `option#toString()` method as an utility build help pages.
438
439### 1.6.2
440
441- Add `EACCES` code to the error thrown on "root" commands.
442
443### 1.6.1
444
445- Fix issue with command option values that started with numbers.
446
447### 1.6.0
448
449- Implement root property support.
450
451### 1.5.0
452
453- Expose internal utils object as `capitano.utils`.
454
455### 1.4.0
456
457- Stdin support. See [#12](https://github.com/resin-io/capitano/issues/12).
458
459Notice `capitano.state.getMatchCommand()` is now async.
460
461### 1.3.1
462
463- Catch action errors and send them to the callback automatically. See [#17](https://github.com/resin-io/capitano/pull/17).
464
465### 1.3.0
466
467- Implement permission support. Discussed in [#15](https://github.com/resin-io/capitano/issues/15).
468
469### 1.2.0
470
471- Implement action callback support. Discussed in [#11](https://github.com/resin-io/capitano/issues/11).
472
473### 1.1.1
474
475- Fix issue with parsing words that start with a number.
476
477### 1.1.0
478
479- Implement support for required options.
480
481### 1.0.4
482
483- Fix issue when instantiating Parameter instances with a number directly.
484
485### 1.0.3
486
487- Fix issue with quotation of multi word parameters. Thanks [@Page-](https://github.com/Page-)!
488
489### 1.0.2
490
491- Fix https://github.com/resin-io/capitano/issues/4.
492
493### 1.0.1
494
495- Fix issues with path and quoted multi string parameters.
496
497Contribute
498----------
499
500- Issue Tracker: [github.com/resin-io/capitano/issues](https://github.com/resin-io/capitano/issues)
501- Source Code: [github.com/resin-io/capitano](https://github.com/resin-io/capitano)
502
503Before submitting a PR, please make sure that you include tests, and that [coffeelint](http://www.coffeelint.org/) runs without any warning:
504
505```sh
506$ gulp lint
507```
508
509Support
510-------
511
512If you're having any problem, please [raise an issue](https://github.com/resin-io/capitano/issues) on GitHub.
513
514TODO
515-------
516
517- Options default values.
518- Allow comma-separated parameter values (numbers, floats, etc).
519
520License
521-------
522
523The project is licensed under the MIT license.