UNPKG

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