UNPKG

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