UNPKG

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