UNPKG

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