UNPKG

14.4 kBMarkdownView Raw
1<img width="945" alt="2017-07-26 9 27 05" src="https://user-images.githubusercontent.com/8784712/28623641-373450f4-7249-11e7-854d-1b076dab274d.png">
2
3[![NPM version](https://img.shields.io/npm/v/cac.svg?style=flat)](https://npmjs.com/package/cac) [![NPM downloads](https://img.shields.io/npm/dm/cac.svg?style=flat)](https://npmjs.com/package/cac) [![CircleCI](https://circleci.com/gh/cacjs/cac/tree/master.svg?style=shield)](https://circleci.com/gh/cacjs/cac/tree/master) [![Codecov](https://badgen.net/codecov/c/github/cacjs/cac/master)](https://codecov.io/gh/cacjs/cac) [![donate](https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat)](https://github.com/egoist/donate) [![chat](https://img.shields.io/badge/chat-on%20discord-7289DA.svg?style=flat)](https://chat.egoist.moe) [![install size](https://badgen.net/packagephobia/install/cac)](https://packagephobia.now.sh/result?p=cac)
4
5## Introduction
6
7**C**ommand **A**nd **C**onquer is a JavaScript library for building CLI apps.
8
9## Features
10
11- **Super light-weight**: No dependency, just a single file.
12- **Easy to learn**. There're only 4 APIs you need to learn for building simple CLIs: `cli.option` `cli.version` `cli.help` `cli.parse`.
13- **Yet so powerful**. Enable features like default command, git-like subcommands, validation for required arguments and options, variadic arguments, dot-nested options, automated help message generation and so on.
14- **Developer friendly**. Written in TypeScript.
15
16## Table of Contents
17
18<!-- toc -->
19
20- [Install](#install)
21- [Usage](#usage)
22 - [Simple Parsing](#simple-parsing)
23 - [Display Help Message and Version](#display-help-message-and-version)
24 - [Command-specific Options](#command-specific-options)
25 - [Brackets](#brackets)
26 - [Variadic Arguments](#variadic-arguments)
27 - [Dot-nested Options](#dot-nested-options)
28 - [Default Command](#default-command)
29 - [Supply an array as option value](#supply-an-array-as-option-value)
30 - [With TypeScript](#with-typescript)
31 - [With Deno](#with-deno)
32- [Projects Using CAC](#projects-using-cac)
33- [References](#references)
34 - [CLI Instance](#cli-instance)
35 - [cac(name?)](#cacname)
36 - [cli.command(name, description, config?)](#clicommandname-description-config)
37 - [cli.option(name, description, config?)](#clioptionname-description-config)
38 - [cli.parse(argv?)](#cliparseargv)
39 - [cli.version(version, customFlags?)](#cliversionversion-customflags)
40 - [cli.help(callback?)](#clihelpcallback)
41 - [cli.outputHelp(subCommand?)](#clioutputhelpsubcommand)
42 - [Command Instance](#command-instance)
43 - [command.option()](#commandoption)
44 - [command.action(callback)](#commandactioncallback)
45 - [command.alias(name)](#commandaliasname)
46 - [command.allowUnknownOptions()](#commandallowunknownoptions)
47 - [command.example(example)](#commandexampleexample)
48 - [Events](#events)
49- [FAQ](#faq)
50 - [How is the name written and pronounced?](#how-is-the-name-written-and-pronounced)
51 - [Why not use Commander.js?](#why-not-use-commanderjs)
52- [Contributing](#contributing)
53- [Author](#author)
54
55<!-- tocstop -->
56
57## Install
58
59```bash
60yarn add cac
61```
62
63## Usage
64
65### Simple Parsing
66
67Use CAC as simple argument parser:
68
69```js
70// examples/basic-usage.js
71const cli = require('cac')()
72
73cli.option('--type <type>', 'Choose a project type', {
74 default: 'node'
75})
76
77const parsed = cli.parse()
78
79console.log(JSON.stringify(parsed, null, 2))
80```
81
82<img width="500" alt="2018-11-26 12 28 03" src="https://user-images.githubusercontent.com/8784712/48981576-2a871000-f112-11e8-8151-80f61e9b9908.png">
83
84### Display Help Message and Version
85
86```js
87// examples/help.js
88const cli = require('cac')()
89
90cli.option('--type [type]', 'Choose a project type', {
91 default: 'node'
92})
93cli.option('--name <name>', 'Provide your name')
94
95cli.command('lint [...files]', 'Lint files').action((files, options) => {
96 console.log(files, options)
97})
98
99// Display help message when `-h` or `--help` appears
100cli.help()
101// Display version number when `-v` or `--version` appears
102// It's also used in help message
103cli.version('0.0.0')
104
105cli.parse()
106```
107
108<img width="500" alt="2018-11-25 8 21 14" src="https://user-images.githubusercontent.com/8784712/48979012-acb20d00-f0ef-11e8-9cc6-8ffca00ab78a.png">
109
110### Command-specific Options
111
112You can attach options to a command.
113
114```js
115const cli = require('cac')()
116
117cli
118 .command('rm <dir>', 'Remove a dir')
119 .option('-r, --recursive', 'Remove recursively')
120 .action((dir, options) => {
121 console.log('remove ' + dir + (options.recursive ? ' recursively' : ''))
122 })
123
124cli.help()
125
126cli.parse()
127```
128
129A command's options are validated when the command is used. Any unknown options will be reported as an error. However, if an action-based command does not define an action, then the options are not validated. If you really want to use unknown options, use [`command.allowUnknownOptions`](#commandallowunknownoptions).
130
131<img alt="command options" width="500" src="https://user-images.githubusercontent.com/8784712/49065552-49dc8500-f259-11e8-9c7b-a7c32d70920e.png">
132
133### Brackets
134
135When using brackets in command name, angled brackets indicate required command arguments, while square bracket indicate optional arguments.
136
137When using brackets in option name, angled brackets indicate that a string / number value is required, while square bracket indicate that the value can also be `true`.
138
139```js
140const cli = require('cac')()
141
142cli
143 .command('deploy <folder>', 'Deploy a folder to AWS')
144 .option('--scale [level]', 'Scaling level')
145 .action((folder, options) => {
146 // ...
147 })
148
149cli
150 .command('build [project]', 'Build a project')
151 .option('--out <dir>', 'Output directory')
152 .action((folder, options) => {
153 // ...
154 })
155
156cli.parse()
157```
158
159To allow an option whose value is `false`, you need to manually speicfy a negated option:
160
161```js
162cli
163 .command('build [project]', 'Build a project')
164 .option('--no-config', 'Disable config file')
165 .option('--config <path>', 'Use a custom config file')
166```
167
168This will let CAC set the default value of `config` to true, and you can use `--no-config` flag to set it to `false`.
169
170### Variadic Arguments
171
172The last argument of a command can be variadic, and only the last argument. To make an argument variadic you have to add `...` to the start of argument name, just like the rest operator in JavaScript. Here is an example:
173
174```js
175const cli = require('cac')()
176
177cli
178 .command('build <entry> [...otherFiles]', 'Build your app')
179 .option('--foo', 'Foo option')
180 .action((entry, otherFiles, options) => {
181 console.log(entry)
182 console.log(otherFiles)
183 console.log(options)
184 })
185
186cli.help()
187
188cli.parse()
189```
190
191<img width="500" alt="2018-11-25 8 25 30" src="https://user-images.githubusercontent.com/8784712/48979056-47125080-f0f0-11e8-9d8f-3219e0beb0ed.png">
192
193### Dot-nested Options
194
195Dot-nested options will be merged into a single option.
196
197```js
198const cli = require('cac')()
199
200cli
201 .command('build', 'desc')
202 .option('--env <env>', 'Set envs')
203 .example('--env.API_SECRET xxx')
204 .action(options => {
205 console.log(options)
206 })
207
208cli.help()
209
210cli.parse()
211```
212
213<img width="500" alt="2018-11-25 9 37 53" src="https://user-images.githubusercontent.com/8784712/48979771-6ada9400-f0fa-11e8-8192-e541b2cfd9da.png">
214
215### Default Command
216
217Register a command that will be used when no other command is matched.
218
219```js
220const cli = require('cac')()
221
222cli
223 // Simply omit the command name, just brackets
224 .command('[...files]', 'Build files')
225 .option('--minimize', 'Minimize output')
226 .action((files, options) => {
227 console.log(files)
228 console.log(options.minimize)
229 })
230
231cli.parse()
232```
233
234### Supply an array as option value
235
236```bash
237node cli.js --include project-a
238# The parsed options will be:
239# { include: 'project-a' }
240
241node cli.js --include project-a --include project-b
242# The parsed options will be:
243# { include: ['project-a', 'project-b'] }
244```
245
246### With TypeScript
247
248First you need `@types/node` to be installed as a dev dependency in your project:
249
250```bash
251yarn add @types/node --dev
252```
253
254Then everything just works out of the box:
255
256```js
257const { cac } = require('cac')
258// OR ES modules
259import { cac } from 'cac'
260```
261
262### With Deno
263
264```ts
265import { cac } from 'https://unpkg.com/cac/mod.js'
266
267// ...
268```
269
270## Projects Using CAC
271
272Projects that use **CAC**:
273
274- [VuePress](https://github.com/vuejs/vuepress): :memo: Minimalistic Vue-powered static site generator.
275- [SAO](https://github.com/egoist/sao): ⚔️ Futuristic scaffolding tool.
276- [DocPad](https://github.com/docpad/docpad): 🏹 Powerful Static Site Generator.
277- [Poi](https://github.com/egoist/poi): ⚡️ Delightful web development.
278- [bili](https://github.com/egoist/bili): 🥂 Schweizer Armeemesser for bundling JavaScript libraries.
279- [lass](https://github.com/lassjs/lass): 💁🏻 Scaffold a modern package boilerplate for Node.js.
280- [Foy](https://github.com/zaaack/foy): 🏗 A lightweight and modern task runner and build tool for general purpose.
281- [Vuese](https://github.com/vuese/vuese): 🤗 One-stop solution for vue component documentation.
282- Feel free to add yours here...
283
284## References
285
286**💁 Check out [the generated docs](https://cac-api-doc.egoist.sh/classes/_cac_.cac.html) from source code if you want a more in-depth API references.**
287
288Below is a brief overview.
289
290### CLI Instance
291
292CLI instance is created by invoking the `cac` function:
293
294```js
295const cac = require('cac')
296const cli = cac()
297```
298
299#### cac(name?)
300
301Create a CLI instance, optionally specify the program name which will be used to display in help and version message. When not set we use the basename of `argv[1]`.
302
303#### cli.command(name, description, config?)
304
305- Type: `(name: string, description: string) => Command`
306
307Create a command instance.
308
309The option also accepts a third argument `config` for additional command config:
310
311- `config.allowUnknownOptions`: `boolean` Allow unknown options in this command.
312- `config.ignoreOptionDefaultValue`: `boolean` Don't use the options's default value in parsed options, only display them in help message.
313
314#### cli.option(name, description, config?)
315
316- Type: `(name: string, description: string, config?: OptionConfig) => CLI`
317
318Add a global option.
319
320The option also accepts a third argument `config` for additional option config:
321
322- `config.default`: Default value for the option.
323- `config.type`: `any[]` When set to `[]`, the option value returns an array type. You can also use a conversion function such as `[String]`, which will invoke the option value with `String`.
324
325#### cli.parse(argv?)
326
327- Type: `(argv = process.argv) => ParsedArgv`
328
329```ts
330interface ParsedArgv {
331 args: string[]
332 options: {
333 [k: string]: any
334 }
335}
336```
337
338When this method is called, `cli.rawArgs` `cli.args` `cli.options` `cli.matchedCommand` will also be available.
339
340#### cli.version(version, customFlags?)
341
342- Type: `(version: string, customFlags = '-v, --version') => CLI`
343
344Output version number when `-v, --version` flag appears.
345
346#### cli.help(callback?)
347
348- Type: `(callback?: HelpCallback) => CLI`
349
350Output help message when `-h, --help` flag appears.
351
352Optional `callback` allows post-processing of help text before it is displayed:
353
354```ts
355type HelpCallback = (sections: HelpSection[]) => void
356
357interface HelpSection {
358 title?: string
359 body: string
360}
361```
362
363#### cli.outputHelp(subCommand?)
364
365- Type: `(subCommand?: boolean) => CLI`
366
367Output help message. Optional `subCommand` argument if you want to output the help message for the matched sub-command instead of the global help message.
368
369### Command Instance
370
371Command instance is created by invoking the `cli.command` method:
372
373```js
374const command = cli.command('build [...files]', 'Build given files')
375```
376
377#### command.option()
378
379Basically the same as `cli.option` but this adds the option to specific command.
380
381#### command.action(callback)
382
383- Type: `(callback: ActionCallback) => Command`
384
385Use a callback function as the command action when the command matches user inputs.
386
387```ts
388type ActionCallback = (
389 // Parsed CLI args
390 // The last arg will be an array if it's an varadic argument
391 ...args: string | string[] | number | number[]
392 // Parsed CLI options
393 options: Options
394) => any
395
396interface Options {
397 [k: string]: any
398}
399```
400
401#### command.alias(name)
402
403- Type: `(name: string) => Command`
404
405Add an alias name to this command, the `name` here can't contain brackets.
406
407#### command.allowUnknownOptions()
408
409- Type: `() => Command`
410
411Allow unknown options in this command, by default CAC will log an error when unknown options are used.
412
413#### command.example(example)
414
415- Type: `(example: CommandExample) => Command`
416
417Add an example which will be displayed at the end of help message.
418
419```ts
420type CommandExample = ((name: string) => string) | string
421```
422
423### Events
424
425Listen to commands:
426
427```js
428// Listen to the `foo` command
429cli.on('command:foo', () => {
430 // Do something
431})
432
433// Listen to the default command
434cli.on('command:!', () => {
435 // Do something
436})
437
438// Listen to unknown commands
439cli.on('command:*', () => {
440 console.error('Invalid command: %', cli.args.join(' '))
441 process.exit(1)
442})
443```
444
445## FAQ
446
447### How is the name written and pronounced?
448
449CAC, or cac, pronounced `C-A-C`.
450
451This project is dedicated to our lovely C.C. sama. Maybe CAC stands for C&C as well :P
452
453<img src="http://i.giphy.com/v3FeH4swox9mg.gif" width="400"/>
454
455### Why not use Commander.js?
456
457CAC is very similar to Commander.js, while the latter does not support dot nested options, i.e. something like `--env.API_SECRET foo`. Besides, you can't use unknown options in Commander.js either.
458
459_And maybe more..._
460
461Basically I made CAC to fulfill my own needs for building CLI apps like [Poi](https://poi.js.org), [SAO](https://saojs.org) and all my CLI apps. It's small, simple but powerful :P
462
463## Contributing
464
4651. Fork it!
4662. Create your feature branch: `git checkout -b my-new-feature`
4673. Commit your changes: `git commit -am 'Add some feature'`
4684. Push to the branch: `git push origin my-new-feature`
4695. Submit a pull request :D
470
471## Author
472
473**CAC** © [EGOIST](https://github.com/egoist), Released under the [MIT](./LICENSE) License.<br>
474Authored and maintained by egoist with help from contributors ([list](https://github.com/cacjs/cac/contributors)).
475
476> [Website](https://egoist.sh) · GitHub [@egoist](https://github.com/egoist) · Twitter [@\_egoistlily](https://twitter.com/_egoistlily)