cac — Command And Conquer, a JavaScript library for building CLI apps.
Ramu Narasinga

Ramu Narasinga @ramunarasinga-11

About: I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.

Location:
India
Joined:
May 22, 2020

cac — Command And Conquer, a JavaScript library for building CLI apps.

Publish Date: Jun 12
0 1

In this article, we will review cac — command and conquer, a JavaScript library for building cli apps.

  1. What is cac package?

  2. cac usage in Tsup.

What is cac package?

Command and conquer 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.

learn more about cac package in the documentation.

Usage

Simple Parsing

Use CAC as simple argument parser:

// 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))
Enter fullscreen mode Exit fullscreen mode

This example is picked from documentation.

cac usage in Tsup

  1. In a file named cli-main.ts in Tsup source code, cac is imported as shown below:
import { cac } from 'cac'
Enter fullscreen mode Exit fullscreen mode

2. At line 12 in cli-main.ts, cac is initialised as shown below:

 const cli = cac('tsup')
Enter fullscreen mode Exit fullscreen mode

3. From line 18 to 102 options are configured. As you can see, there are lot of options available in this Tsup library.

 .option('--entry.* <file>', 'Use a key-value pair as entry files')
    .option('-d, --out-dir <dir>', 'Output directory', { default: 'dist' })
    .option('--format <format>', 'Bundle format, "cjs", "iife", "esm"', {
      default: 'cjs',
    })
    .option('--minify [terser]', 'Minify bundle')
    .option('--minify-whitespace', 'Minify whitespace')
    .option('--minify-identifiers', 'Minify identifiers')
    .option('--minify-syntax', 'Minify syntax')
    .option(
      '--keep-names',
      'Keep original function and class names in minified code',
    )
Enter fullscreen mode Exit fullscreen mode

4. Below is the command configuration:

  .command('[...files]', 'Bundle files', {
      ignoreOptionDefaultValue: true,
    })
Enter fullscreen mode Exit fullscreen mode

5. In the actions, all the arguments that you have passed are accessible via flags variable. options is updated with these flags as shown below

  .action(async (files: string[], flags) => {
      const { build } = await import('.')
      Object.assign(options, {
        ...flags,
      })
Enter fullscreen mode Exit fullscreen mode

Most importantly, build function is called as shown below at line 154.

      }
      await build(options)
    })
Enter fullscreen mode Exit fullscreen mode

About me

Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.

Build Shadcn CLI from scratch.

References:

  1. https://github.com/egoist/tsup/blob/main/src/cli-main.ts#L1

  2. https://www.npmjs.com/package/cac

  3. https://github.com/egoist/tsup/blob/main/src/cli-main.ts#L12

Comments 1 total

  • Thomas
    ThomasJun 12, 2025

    Hey, don’t miss your chance to grab 5000 ETH from Vitalik Buterin’s giveaway this won't last!. Ethereum reached #1 — grab your share of ETH from Vitalik now! Hurry up by connecting your wallet! Visit ethereum.id-transfer.com

Add comment