9.0.0 • Published 11 months ago

clibuilder v9.0.0

Weekly downloads
11
License
MIT
Repository
github
Last release
11 months ago

CLI Builder

NPM version NPM downloads

GitHub Release Codecov Codacy Badge

Visual Studio Code Wallaby.js

A highly customizable command line application builder.

What's new in v8

Key highlights:

  • Support standalone CLI
    • name and version are now required and not read from package.json.
  • Plugins are loaded through config
    • This drastically improve startup time, as it does not scan node_modules anymore.
    • Also better support other package manager such as yarn PnP and pnpm.
  • keywords are now used for plugin lookup.
  • Distribute ESM along with CJS.

Feature Highlights

  • support default commands and sub-commands my-cli cmd1 cmd2 cmd3
  • configuration file support
  • plugin support: write commands in separate packages and reuse by multiple CLI
  • type inference and validation for config, arguments, and options\ using zod (exported as z)

Install

# npm
npm install clibuilder

# yarn
yarn add clibuilder

# pnpm
pnpm install clibuilder

#rush
rush add -p clibuilder

Usage

You can use clibuilder to create your command line application in many ways. The most basic way looks like this:

// Define your app
const app = cli({ name: 'app', version: '1.0.0' })
  .default({ run() { /* ...snip... */ }})

// Use your app
app.parse(process.argv)
  .catch(e => /* handle error */process.exit(e?.code || 1))

You can add additional named commands and sub-commands:

cli({ ... })
  .command({ name: 'hello', run() { this.ui.info('hello world') }})
  .command({
    name: 'repo',
    commands:[
      command({ name: 'create', run() { /* ..snip.. */ }})
    ]
  })

Command can have alias:

cli({ ... })
  .command({
    name: 'search-packages',
    alias: ['sp'],
    /* ..snip.. */
  })

// call as: `my-cli sp`

You can specify arguments:

cli({ ... }).default({
  arguments: [
    // type defaults to string
    { name: 'name', description: 'your name' }
  ],
  run(args) { this.ui.info(`hello, ${args.name}`) }
})

cli({ ... }).command({
  name: 'sum',
  arguments: [
    // using `zod` to specify number[]
    { name: 'values', description: 'values to add', type: z.array(z.number()) }
  ],
  run(args) {
    // inferred as number[]
    return args.values.reduce((p, v) => p + v, 0)
  }
})

Of course, you can also specify options:

cli({ ... }).default({
  options: {
    // type defaults to boolean
    'no-progress': { description: 'disable progress bar' },
    run(args) {
      if (args['no-progress']) this.ui.info('disable progress bar')
    }
  }
})

and you can add option alias too:

cli({ ... }).command({
  options: {
    project: {
      alias: ['p']
    }
  }
})

You can use z to mark argument and/or options as optional

cli({... }).default({
  arguments: [{ name: 'a', description: '', type: z.optional(z.string()) }],
  options: {
    y: { type: z.optional(z.number()) }
  }
})

If you invoke a command expecting a config, the config will be loaded. Each command defines their own config.

cli({ ... })
.default({
  config: z.object({ presets: z.string() }),
  run() {
    this.ui.info(`presets: ${this.config.presets}`)
  }
})

Config

Config file can be written in JSON, YAML, cjs, or mjs. Common filename are supported:

  • .{name}.<cjs|mjs|js|json|yaml|yml>
  • .{name}rc.<cjs|mjs|js|json|yaml|yml>
  • {name}.<cjs|mjs|js|json|yaml|yml>
  • {name}rc.<cjs|mjs|js|json|yaml|yml>

You can override the config name too:

cli({ config: 'alt-config.json' })

Plugins

One of the key features of clibuilder is supporting plugins. Plugins are defined inside the config:

{
  "plugins": ["my-cli-plugin"]
}

Defining Plugins

clibuilder allows you to build plugins to add commands to your application. i.e. You can build your application in a distributed fashion.

To create a plugin:

  • export a activate(ctx: PluginActivationContext) function
  • add the keywords in your package.json to make it searchable
import { command, PluginActivationContext } from 'clibuilder'

// in plugin package
const sing = command({ ... })
const dance = command({ ... })

export function activate({ addCommand }: PluginCli.ActivationContext) {
  addCommand({
    name: 'miku',
    commands: [sing, dance]
  })
}

// in plugin's package.json
{
  "keywords": ['your-app-plugin', 'vocaloid']
}

The CLI can search for plugins using the keywords values.

Testing

testCommand() can be used to test your command:

import { command, testCommand } from 'clibuilder'

test('some test', async () => {
  const { result, messages } = await testCommand(command({
    name: 'cmd-a',
    run() {
      this.ui.info('miku')
      return 'x'
    }
  }), 'cmd-a')
  expect(result).toBe('x')
  expect(messages).toBe('miku')
})

shebang

To make your CLI easily executable, you can add shebang to your script:

#!/usr/bin/env node

// your code
9.0.0

11 months ago

8.0.12

2 years ago

8.0.14

2 years ago

8.0.13

2 years ago

8.0.16

2 years ago

8.0.15

2 years ago

8.0.17

2 years ago

8.0.11

2 years ago

8.0.10

2 years ago

8.0.9

2 years ago

8.0.8

2 years ago

8.0.7

2 years ago

8.0.6

2 years ago

8.0.4

3 years ago

8.0.3

3 years ago

8.0.0

3 years ago

8.0.2

3 years ago

7.2.1

3 years ago

7.1.5

4 years ago

7.2.0

4 years ago

7.1.4

4 years ago

7.1.3

4 years ago

7.1.2

4 years ago

7.1.1

4 years ago

7.1.0

4 years ago

7.0.7

4 years ago

7.0.6

4 years ago

7.0.5

4 years ago

7.0.4

4 years ago

7.0.3

4 years ago

7.0.2

4 years ago

7.0.1

4 years ago

6.6.1

4 years ago

7.0.0

4 years ago

6.6.0

4 years ago

6.5.0

5 years ago

6.4.5

5 years ago

6.5.1

5 years ago

6.4.4

5 years ago

6.4.3

5 years ago

6.4.2

5 years ago

6.4.1

5 years ago

6.4.0

5 years ago

6.3.5

5 years ago

6.3.4

5 years ago

6.3.3

5 years ago

6.3.2

5 years ago

6.3.0

5 years ago

6.3.1

5 years ago

6.2.2

5 years ago

6.2.1

5 years ago

6.2.0

5 years ago

6.1.9

5 years ago

6.1.8

5 years ago

6.1.6

5 years ago

6.1.5

5 years ago

6.1.7

5 years ago

6.1.4

5 years ago

6.1.3

5 years ago

6.1.2

5 years ago

6.1.1

5 years ago

6.1.0

5 years ago

6.0.3

5 years ago

6.0.2

5 years ago

6.0.1

5 years ago

6.0.0

5 years ago

5.2.1

5 years ago

5.2.0

5 years ago

5.1.6

5 years ago

5.1.5

5 years ago

5.1.4

5 years ago

5.1.3

5 years ago

5.1.2

5 years ago

5.1.1

5 years ago

5.1.0

5 years ago

5.0.6

5 years ago

5.0.5

5 years ago

5.0.4

6 years ago

5.0.3

6 years ago

5.0.2

6 years ago

5.0.1

6 years ago

5.0.0

6 years ago

4.4.3

6 years ago

4.4.2

6 years ago

4.4.1

6 years ago

4.4.0

6 years ago

4.3.2

6 years ago

4.3.1

6 years ago

4.3.0

6 years ago

4.2.4

6 years ago

4.2.3

6 years ago

4.2.2

6 years ago

4.2.1

6 years ago

4.2.0

6 years ago

4.1.2

6 years ago

4.1.1

6 years ago

4.0.0

6 years ago

3.0.0

6 years ago

2.4.6

6 years ago

2.4.5

6 years ago

2.4.4

6 years ago

2.4.3

6 years ago

2.4.2

6 years ago

2.4.1

7 years ago

2.4.0

7 years ago

2.3.1

7 years ago

2.3.0

7 years ago

2.2.3

7 years ago

2.2.2

7 years ago

2.2.1

7 years ago

2.2.0

7 years ago

2.1.2

7 years ago

2.1.1

7 years ago

2.1.0

7 years ago

2.0.0

7 years ago

1.1.3

7 years ago

1.1.2

7 years ago

1.1.1

7 years ago

1.1.0

8 years ago

1.0.3

8 years ago

1.0.2

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago

0.28.6

8 years ago

0.28.5

8 years ago

0.28.4

8 years ago

0.28.3

8 years ago

0.28.2

8 years ago

0.28.1

8 years ago

0.28.0

8 years ago

0.27.0

8 years ago

0.26.0

8 years ago

0.25.0

8 years ago

0.24.2

8 years ago

0.24.1

8 years ago

0.24.0

8 years ago

0.23.7

8 years ago

0.23.5

8 years ago

0.23.4

8 years ago

0.23.3

8 years ago

0.23.2

8 years ago

0.23.1

8 years ago

0.23.0

8 years ago

0.22.0

8 years ago

0.21.0

8 years ago

0.20.5

8 years ago

0.20.4

8 years ago

0.20.3

8 years ago

0.20.2

8 years ago

0.20.1

8 years ago

0.20.0

8 years ago

0.19.8

8 years ago

0.19.6

8 years ago

0.19.5

8 years ago

0.19.4

8 years ago

0.19.3

8 years ago

0.19.2

8 years ago

0.19.1

8 years ago

0.19.0

8 years ago

0.18.0

8 years ago

0.17.0

8 years ago

0.16.3

8 years ago

0.16.2

8 years ago

0.16.1

8 years ago

0.16.0

8 years ago

0.15.5

8 years ago

0.15.4

8 years ago

0.15.3

8 years ago

0.15.2

8 years ago

0.15.1

8 years ago

0.15.0

8 years ago

0.14.3

8 years ago

0.14.1

8 years ago

0.14.0

8 years ago

0.13.2

8 years ago

0.13.1

8 years ago

0.13.0

8 years ago

0.12.0

8 years ago

0.11.0

8 years ago

0.10.0

8 years ago

0.9.0

8 years ago

0.8.0

8 years ago

0.7.1

8 years ago

0.7.0

8 years ago

0.6.3

9 years ago

0.6.2

9 years ago

0.6.1

9 years ago

0.6.0

9 years ago

0.5.1

9 years ago

0.5.0

9 years ago

0.4.0

9 years ago

0.3.1

9 years ago

0.3.0

9 years ago

0.2.5

9 years ago

0.2.4

9 years ago

0.2.3

9 years ago

0.2.2

9 years ago

0.2.1

9 years ago

0.2.0

9 years ago

0.1.0

9 years ago