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- [NUT](https://github.com/nut-project/nut): 🌰 A framework born for microfrontends
283- Feel free to add yours here...
284
285## References
286
287**💁 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.**
288
289Below is a brief overview.
290
291### CLI Instance
292
293CLI instance is created by invoking the `cac` function:
294
295```js
296const cac = require('cac')
297const cli = cac()
298```
299
300#### cac(name?)
301
302Create 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]`.
303
304#### cli.command(name, description, config?)
305
306- Type: `(name: string, description: string) => Command`
307
308Create a command instance.
309
310The option also accepts a third argument `config` for additional command config:
311
312- `config.allowUnknownOptions`: `boolean` Allow unknown options in this command.
313- `config.ignoreOptionDefaultValue`: `boolean` Don't use the options's default value in parsed options, only display them in help message.
314
315#### cli.option(name, description, config?)
316
317- Type: `(name: string, description: string, config?: OptionConfig) => CLI`
318
319Add a global option.
320
321The option also accepts a third argument `config` for additional option config:
322
323- `config.default`: Default value for the option.
324- `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`.
325
326#### cli.parse(argv?)
327
328- Type: `(argv = process.argv) => ParsedArgv`
329
330```ts
331interface ParsedArgv {
332 args: string[]
333 options: {
334 [k: string]: any
335 }
336}
337```
338
339When this method is called, `cli.rawArgs` `cli.args` `cli.options` `cli.matchedCommand` will also be available.
340
341#### cli.version(version, customFlags?)
342
343- Type: `(version: string, customFlags = '-v, --version') => CLI`
344
345Output version number when `-v, --version` flag appears.
346
347#### cli.help(callback?)
348
349- Type: `(callback?: HelpCallback) => CLI`
350
351Output help message when `-h, --help` flag appears.
352
353Optional `callback` allows post-processing of help text before it is displayed:
354
355```ts
356type HelpCallback = (sections: HelpSection[]) => void
357
358interface HelpSection {
359 title?: string
360 body: string
361}
362```
363
364#### cli.outputHelp(subCommand?)
365
366- Type: `(subCommand?: boolean) => CLI`
367
368Output help message. Optional `subCommand` argument if you want to output the help message for the matched sub-command instead of the global help message.
369
370### Command Instance
371
372Command instance is created by invoking the `cli.command` method:
373
374```js
375const command = cli.command('build [...files]', 'Build given files')
376```
377
378#### command.option()
379
380Basically the same as `cli.option` but this adds the option to specific command.
381
382#### command.action(callback)
383
384- Type: `(callback: ActionCallback) => Command`
385
386Use a callback function as the command action when the command matches user inputs.
387
388```ts
389type ActionCallback = (
390 // Parsed CLI args
391 // The last arg will be an array if it's an varadic argument
392 ...args: string | string[] | number | number[]
393 // Parsed CLI options
394 options: Options
395) => any
396
397interface Options {
398 [k: string]: any
399}
400```
401
402#### command.alias(name)
403
404- Type: `(name: string) => Command`
405
406Add an alias name to this command, the `name` here can't contain brackets.
407
408#### command.allowUnknownOptions()
409
410- Type: `() => Command`
411
412Allow unknown options in this command, by default CAC will log an error when unknown options are used.
413
414#### command.example(example)
415
416- Type: `(example: CommandExample) => Command`
417
418Add an example which will be displayed at the end of help message.
419
420```ts
421type CommandExample = ((name: string) => string) | string
422```
423
424### Events
425
426Listen to commands:
427
428```js
429// Listen to the `foo` command
430cli.on('command:foo', () => {
431 // Do something
432})
433
434// Listen to the default command
435cli.on('command:!', () => {
436 // Do something
437})
438
439// Listen to unknown commands
440cli.on('command:*', () => {
441 console.error('Invalid command: %', cli.args.join(' '))
442 process.exit(1)
443})
444```
445
446## FAQ
447
448### How is the name written and pronounced?
449
450CAC, or cac, pronounced `C-A-C`.
451
452This project is dedicated to our lovely C.C. sama. Maybe CAC stands for C&C as well :P
453
454<img src="http://i.giphy.com/v3FeH4swox9mg.gif" width="400"/>
455
456### Why not use Commander.js?
457
458CAC 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.
459
460_And maybe more..._
461
462Basically 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
463
464## Contributing
465
4661. Fork it!
4672. Create your feature branch: `git checkout -b my-new-feature`
4683. Commit your changes: `git commit -am 'Add some feature'`
4694. Push to the branch: `git push origin my-new-feature`
4705. Submit a pull request :D
471
472## Author
473
474**CAC** © [EGOIST](https://github.com/egoist), Released under the [MIT](./LICENSE) License.<br>
475Authored and maintained by egoist with help from contributors ([list](https://github.com/cacjs/cac/contributors)).
476
477> [Website](https://egoist.sh) · GitHub [@egoist](https://github.com/egoist) · Twitter [@\_egoistlily](https://twitter.com/_egoistlily)