UNPKG

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