UNPKG

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