| <img width="945" alt="2017-07-26 9 27 05" src="https://user-images.githubusercontent.com/8784712/28623641-373450f4-7249-11e7-854d-1b076dab274d.png"> | |
| [](https://npmjs.com/package/cac) [](https://npmjs.com/package/cac) [](https://circleci.com/gh/cacjs/cac/tree/master) [](https://codecov.io/gh/cacjs/cac) [](https://github.com/egoist/donate) [](https://chat.egoist.moe) [](https://packagephobia.now.sh/result?p=cac) | |
| ## Introduction | |
| **C**ommand **A**nd **C**onquer is a JavaScript library for building CLI apps. | |
| ## Features | |
| - **Super light-weight**: No dependency, just a single file. | |
| - **Easy to learn**. There're only 4 APIs you need to learn for building simple CLIs: `cli.option` `cli.version` `cli.help` `cli.parse`. | |
| - **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. | |
| - **Developer friendly**. Written in TypeScript. | |
| ## Table of Contents | |
| <!-- toc --> | |
| - [Install](#install) | |
| - [Usage](#usage) | |
| - [Simple Parsing](#simple-parsing) | |
| - [Display Help Message and Version](#display-help-message-and-version) | |
| - [Command-specific Options](#command-specific-options) | |
| - [Dash in option names](#dash-in-option-names) | |
| - [Brackets](#brackets) | |
| - [Negated Options](#negated-options) | |
| - [Variadic Arguments](#variadic-arguments) | |
| - [Dot-nested Options](#dot-nested-options) | |
| - [Default Command](#default-command) | |
| - [Supply an array as option value](#supply-an-array-as-option-value) | |
| - [Error Handling](#error-handling) | |
| - [With TypeScript](#with-typescript) | |
| - [With Deno](#with-deno) | |
| - [Projects Using CAC](#projects-using-cac) | |
| - [References](#references) | |
| - [CLI Instance](#cli-instance) | |
| - [cac(name?)](#cacname) | |
| - [cli.command(name, description, config?)](#clicommandname-description-config) | |
| - [cli.option(name, description, config?)](#clioptionname-description-config) | |
| - [cli.parse(argv?)](#cliparseargv) | |
| - [cli.version(version, customFlags?)](#cliversionversion-customflags) | |
| - [cli.help(callback?)](#clihelpcallback) | |
| - [cli.outputHelp()](#clioutputhelp) | |
| - [cli.usage(text)](#cliusagetext) | |
| - [Command Instance](#command-instance) | |
| - [command.option()](#commandoption) | |
| - [command.action(callback)](#commandactioncallback) | |
| - [command.alias(name)](#commandaliasname) | |
| - [command.allowUnknownOptions()](#commandallowunknownoptions) | |
| - [command.example(example)](#commandexampleexample) | |
| - [command.usage(text)](#commandusagetext) | |
| - [Events](#events) | |
| - [FAQ](#faq) | |
| - [How is the name written and pronounced?](#how-is-the-name-written-and-pronounced) | |
| - [Why not use Commander.js?](#why-not-use-commanderjs) | |
| - [Project Stats](#project-stats) | |
| - [Contributing](#contributing) | |
| - [Author](#author) | |
| <!-- tocstop --> | |
| ## Install | |
| ```bash | |
| yarn add cac | |
| ``` | |
| ## Usage | |
| ### Simple Parsing | |
| Use CAC as simple argument parser: | |
| ```js | |
| // examples/basic-usage.js | |
| const cli = require('cac')() | |
| cli.option('--type <type>', 'Choose a project type', { | |
| default: 'node', | |
| }) | |
| const parsed = cli.parse() | |
| console.log(JSON.stringify(parsed, null, 2)) | |
| ``` | |
| <img width="500" alt="2018-11-26 12 28 03" src="https://user-images.githubusercontent.com/8784712/48981576-2a871000-f112-11e8-8151-80f61e9b9908.png"> | |
| ### Display Help Message and Version | |
| ```js | |
| // examples/help.js | |
| const cli = require('cac')() | |
| cli.option('--type [type]', 'Choose a project type', { | |
| default: 'node', | |
| }) | |
| cli.option('--name <name>', 'Provide your name') | |
| cli.command('lint [...files]', 'Lint files').action((files, options) => { | |
| console.log(files, options) | |
| }) | |
| // Display help message when `-h` or `--help` appears | |
| cli.help() | |
| // Display version number when `-v` or `--version` appears | |
| // It's also used in help message | |
| cli.version('0.0.0') | |
| cli.parse() | |
| ``` | |
| <img width="500" alt="2018-11-25 8 21 14" src="https://user-images.githubusercontent.com/8784712/48979012-acb20d00-f0ef-11e8-9cc6-8ffca00ab78a.png"> | |
| ### Command-specific Options | |
| You can attach options to a command. | |
| ```js | |
| const cli = require('cac')() | |
| cli | |
| .command('rm <dir>', 'Remove a dir') | |
| .option('-r, --recursive', 'Remove recursively') | |
| .action((dir, options) => { | |
| console.log('remove ' + dir + (options.recursive ? ' recursively' : '')) | |
| }) | |
| cli.help() | |
| cli.parse() | |
| ``` | |
| A 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). | |
| <img alt="command options" width="500" src="https://user-images.githubusercontent.com/8784712/49065552-49dc8500-f259-11e8-9c7b-a7c32d70920e.png"> | |
| ### Dash in option names | |
| Options in kebab-case should be referenced in camelCase in your code: | |
| ```js | |
| cli | |
| .command('dev', 'Start dev server') | |
| .option('--clear-screen', 'Clear screen') | |
| .action((options) => { | |
| console.log(options.clearScreen) | |
| }) | |
| ``` | |
| In fact `--clear-screen` and `--clearScreen` are both mapped to `options.clearScreen`. | |
| ### Brackets | |
| When using brackets in command name, angled brackets indicate required command arguments, while square bracket indicate optional arguments. | |
| When 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`. | |
| ```js | |
| const cli = require('cac')() | |
| cli | |
| .command('deploy <folder>', 'Deploy a folder to AWS') | |
| .option('--scale [level]', 'Scaling level') | |
| .action((folder, options) => { | |
| // ... | |
| }) | |
| cli | |
| .command('build [project]', 'Build a project') | |
| .option('--out <dir>', 'Output directory') | |
| .action((folder, options) => { | |
| // ... | |
| }) | |
| cli.parse() | |
| ``` | |
| ### Negated Options | |
| To allow an option whose value is `false`, you need to manually specify a negated option: | |
| ```js | |
| cli | |
| .command('build [project]', 'Build a project') | |
| .option('--no-config', 'Disable config file') | |
| .option('--config <path>', 'Use a custom config file') | |
| ``` | |
| This will let CAC set the default value of `config` to true, and you can use `--no-config` flag to set it to `false`. | |
| ### Variadic Arguments | |
| The 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: | |
| ```js | |
| const cli = require('cac')() | |
| cli | |
| .command('build <entry> [...otherFiles]', 'Build your app') | |
| .option('--foo', 'Foo option') | |
| .action((entry, otherFiles, options) => { | |
| console.log(entry) | |
| console.log(otherFiles) | |
| console.log(options) | |
| }) | |
| cli.help() | |
| cli.parse() | |
| ``` | |
| <img width="500" alt="2018-11-25 8 25 30" src="https://user-images.githubusercontent.com/8784712/48979056-47125080-f0f0-11e8-9d8f-3219e0beb0ed.png"> | |
| ### Dot-nested Options | |
| Dot-nested options will be merged into a single option. | |
| ```js | |
| const cli = require('cac')() | |
| cli | |
| .command('build', 'desc') | |
| .option('--env <env>', 'Set envs') | |
| .example('--env.API_SECRET xxx') | |
| .action((options) => { | |
| console.log(options) | |
| }) | |
| cli.help() | |
| cli.parse() | |
| ``` | |
| <img width="500" alt="2018-11-25 9 37 53" src="https://user-images.githubusercontent.com/8784712/48979771-6ada9400-f0fa-11e8-8192-e541b2cfd9da.png"> | |
| ### Default Command | |
| Register a command that will be used when no other command is matched. | |
| ```js | |
| const cli = require('cac')() | |
| cli | |
| // Simply omit the command name, just brackets | |
| .command('[...files]', 'Build files') | |
| .option('--minimize', 'Minimize output') | |
| .action((files, options) => { | |
| console.log(files) | |
| console.log(options.minimize) | |
| }) | |
| cli.parse() | |
| ``` | |
| ### Supply an array as option value | |
| ```bash | |
| node cli.js --include project-a | |
| # The parsed options will be: | |
| # { include: 'project-a' } | |
| node cli.js --include project-a --include project-b | |
| # The parsed options will be: | |
| # { include: ['project-a', 'project-b'] } | |
| ``` | |
| ### Error Handling | |
| To handle command errors globally: | |
| ```js | |
| try { | |
| // Parse CLI args without running the command | |
| cli.parse(process.argv, { run: false }) | |
| // Run the command yourself | |
| // You only need `await` when your command action returns a Promise | |
| await cli.runMatchedCommand() | |
| } catch (error) { | |
| // Handle error here.. | |
| // e.g. | |
| // console.error(error.stack) | |
| // process.exit(1) | |
| } | |
| ``` | |
| ### With TypeScript | |
| First you need `@types/node` to be installed as a dev dependency in your project: | |
| ```bash | |
| yarn add @types/node --dev | |
| ``` | |
| Then everything just works out of the box: | |
| ```js | |
| const { cac } = require('cac') | |
| // OR ES modules | |
| import { cac } from 'cac' | |
| ``` | |
| ### With Deno | |
| ```ts | |
| import { cac } from 'https://unpkg.com/cac/mod.ts' | |
| const cli = cac('my-program') | |
| ``` | |
| ## Projects Using CAC | |
| Projects that use **CAC**: | |
| - [VuePress](https://github.com/vuejs/vuepress): :memo: Minimalistic Vue-powered static site generator. | |
| - [SAO](https://github.com/egoist/sao): ⚔️ Futuristic scaffolding tool. | |
| - [DocPad](https://github.com/docpad/docpad): 🏹 Powerful Static Site Generator. | |
| - [Poi](https://github.com/egoist/poi): ⚡️ Delightful web development. | |
| - [bili](https://github.com/egoist/bili): 🥂 Schweizer Armeemesser for bundling JavaScript libraries. | |
| - [Lad](https://github.com/ladjs/lad): 👦 Lad scaffolds a Koa webapp and API framework for Node.js. | |
| - [Lass](https://github.com/lassjs/lass): 💁🏻 Scaffold a modern package boilerplate for Node.js. | |
| - [Foy](https://github.com/zaaack/foy): 🏗 A lightweight and modern task runner and build tool for general purpose. | |
| - [Vuese](https://github.com/vuese/vuese): 🤗 One-stop solution for vue component documentation. | |
| - [NUT](https://github.com/nut-project/nut): 🌰 A framework born for microfrontends | |
| - Feel free to add yours here... | |
| ## References | |
| **💁 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.** | |
| Below is a brief overview. | |
| ### CLI Instance | |
| CLI instance is created by invoking the `cac` function: | |
| ```js | |
| const cac = require('cac') | |
| const cli = cac() | |
| ``` | |
| #### cac(name?) | |
| Create 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]`. | |
| #### cli.command(name, description, config?) | |
| - Type: `(name: string, description: string) => Command` | |
| Create a command instance. | |
| The option also accepts a third argument `config` for additional command config: | |
| - `config.allowUnknownOptions`: `boolean` Allow unknown options in this command. | |
| - `config.ignoreOptionDefaultValue`: `boolean` Don't use the options's default value in parsed options, only display them in help message. | |
| #### cli.option(name, description, config?) | |
| - Type: `(name: string, description: string, config?: OptionConfig) => CLI` | |
| Add a global option. | |
| The option also accepts a third argument `config` for additional option config: | |
| - `config.default`: Default value for the option. | |
| - `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`. | |
| #### cli.parse(argv?) | |
| - Type: `(argv = process.argv) => ParsedArgv` | |
| ```ts | |
| interface ParsedArgv { | |
| args: string[] | |
| options: { | |
| [k: string]: any | |
| } | |
| } | |
| ``` | |
| When this method is called, `cli.rawArgs` `cli.args` `cli.options` `cli.matchedCommand` will also be available. | |
| #### cli.version(version, customFlags?) | |
| - Type: `(version: string, customFlags = '-v, --version') => CLI` | |
| Output version number when `-v, --version` flag appears. | |
| #### cli.help(callback?) | |
| - Type: `(callback?: HelpCallback) => CLI` | |
| Output help message when `-h, --help` flag appears. | |
| Optional `callback` allows post-processing of help text before it is displayed: | |
| ```ts | |
| type HelpCallback = (sections: HelpSection[]) => void | |
| interface HelpSection { | |
| title?: string | |
| body: string | |
| } | |
| ``` | |
| #### cli.outputHelp() | |
| - Type: `() => CLI` | |
| Output help message. | |
| #### cli.usage(text) | |
| - Type: `(text: string) => CLI` | |
| Add a global usage text. This is not used by sub-commands. | |
| ### Command Instance | |
| Command instance is created by invoking the `cli.command` method: | |
| ```js | |
| const command = cli.command('build [...files]', 'Build given files') | |
| ``` | |
| #### command.option() | |
| Basically the same as `cli.option` but this adds the option to specific command. | |
| #### command.action(callback) | |
| - Type: `(callback: ActionCallback) => Command` | |
| Use a callback function as the command action when the command matches user inputs. | |
| ```ts | |
| type ActionCallback = ( | |
| // Parsed CLI args | |
| // The last arg will be an array if it's a variadic argument | |
| ...args: string | string[] | number | number[] | |
| // Parsed CLI options | |
| options: Options | |
| ) => any | |
| interface Options { | |
| [k: string]: any | |
| } | |
| ``` | |
| #### command.alias(name) | |
| - Type: `(name: string) => Command` | |
| Add an alias name to this command, the `name` here can't contain brackets. | |
| #### command.allowUnknownOptions() | |
| - Type: `() => Command` | |
| Allow unknown options in this command, by default CAC will log an error when unknown options are used. | |
| #### command.example(example) | |
| - Type: `(example: CommandExample) => Command` | |
| Add an example which will be displayed at the end of help message. | |
| ```ts | |
| type CommandExample = ((name: string) => string) | string | |
| ``` | |
| #### command.usage(text) | |
| - Type: `(text: string) => Command` | |
| Add a usage text for this command. | |
| ### Events | |
| Listen to commands: | |
| ```js | |
| // Listen to the `foo` command | |
| cli.on('command:foo', () => { | |
| // Do something | |
| }) | |
| // Listen to the default command | |
| cli.on('command:!', () => { | |
| // Do something | |
| }) | |
| // Listen to unknown commands | |
| cli.on('command:*', () => { | |
| console.error('Invalid command: %s', cli.args.join(' ')) | |
| process.exit(1) | |
| }) | |
| ``` | |
| ## FAQ | |
| ### How is the name written and pronounced? | |
| CAC, or cac, pronounced `C-A-C`. | |
| This project is dedicated to our lovely C.C. sama. Maybe CAC stands for C&C as well :P | |
| <img src="http://i.giphy.com/v3FeH4swox9mg.gif" width="400"/> | |
| ### Why not use Commander.js? | |
| CAC 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. | |
| _And maybe more..._ | |
| Basically I made CAC to fulfill my own needs for building CLI apps like [Poi](https://poi.js.org), [SAO](https://sao.vercel.app) and all my CLI apps. It's small, simple but powerful :P | |
| ## Project Stats | |
|  | |
| ## Contributing | |
| 1. Fork it! | |
| 2. Create your feature branch: `git checkout -b my-new-feature` | |
| 3. Commit your changes: `git commit -am 'Add some feature'` | |
| 4. Push to the branch: `git push origin my-new-feature` | |
| 5. Submit a pull request :D | |
| ## Author | |
| **CAC** © [EGOIST](https://github.com/egoist), Released under the [MIT](./LICENSE) License.<br> | |
| Authored and maintained by egoist with help from contributors ([list](https://github.com/cacjs/cac/contributors)). | |
| > [Website](https://egoist.sh) · GitHub [@egoist](https://github.com/egoist) · Twitter [@\_egoistlily](https://twitter.com/_egoistlily) | |