8.0.17 • Published 1 year ago

clibuilder v8.0.17

Weekly downloads
11
License
MIT
Repository
github
Last release
1 year 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
8.0.12

1 year ago

8.0.14

1 year ago

8.0.13

1 year ago

8.0.16

1 year ago

8.0.15

1 year ago

8.0.17

1 year ago

8.0.11

1 year ago

8.0.10

1 year ago

8.0.9

1 year ago

8.0.8

1 year ago

8.0.7

1 year ago

8.0.6

1 year ago

8.0.4

2 years ago

8.0.3

2 years ago

8.0.0

2 years ago

8.0.2

2 years ago

7.2.1

2 years ago

7.1.5

3 years ago

7.2.0

3 years ago

7.1.4

3 years ago

7.1.3

3 years ago

7.1.2

3 years ago

7.1.1

3 years ago

7.1.0

3 years ago

7.0.7

3 years ago

7.0.6

3 years ago

7.0.5

3 years ago

7.0.4

3 years ago

7.0.3

3 years ago

7.0.2

3 years ago

7.0.1

3 years ago

6.6.1

3 years ago

7.0.0

3 years ago

6.6.0

3 years ago

6.5.0

4 years ago

6.4.5

4 years ago

6.5.1

4 years ago

6.4.4

4 years ago

6.4.3

4 years ago

6.4.2

4 years ago

6.4.1

4 years ago

6.4.0

4 years ago

6.3.5

4 years ago

6.3.4

4 years ago

6.3.3

4 years ago

6.3.2

4 years ago

6.3.0

4 years ago

6.3.1

4 years ago

6.2.2

4 years ago

6.2.1

4 years ago

6.2.0

4 years ago

6.1.9

4 years ago

6.1.8

4 years ago

6.1.6

4 years ago

6.1.5

4 years ago

6.1.7

4 years ago

6.1.4

4 years ago

6.1.3

4 years ago

6.1.2

4 years ago

6.1.1

4 years ago

6.1.0

4 years ago

6.0.3

4 years ago

6.0.2

4 years ago

6.0.1

4 years ago

6.0.0

4 years ago

5.2.1

4 years ago

5.2.0

4 years ago

5.1.6

4 years ago

5.1.5

4 years ago

5.1.4

4 years ago

5.1.3

4 years ago

5.1.2

4 years ago

5.1.1

4 years ago

5.1.0

4 years ago

5.0.6

4 years ago

5.0.5

4 years ago

5.0.4

5 years ago

5.0.3

5 years ago

5.0.2

5 years ago

5.0.1

5 years ago

5.0.0

5 years ago

4.4.3

5 years ago

4.4.2

5 years ago

4.4.1

5 years ago

4.4.0

5 years ago

4.3.2

5 years ago

4.3.1

5 years ago

4.3.0

5 years ago

4.2.4

5 years ago

4.2.3

5 years ago

4.2.2

5 years ago

4.2.1

5 years ago

4.2.0

5 years ago

4.1.2

5 years ago

4.1.1

5 years ago

4.0.0

5 years ago

3.0.0

5 years ago

2.4.6

5 years ago

2.4.5

5 years ago

2.4.4

5 years ago

2.4.3

5 years ago

2.4.2

5 years ago

2.4.1

5 years ago

2.4.0

5 years ago

2.3.1

5 years ago

2.3.0

6 years ago

2.2.3

6 years ago

2.2.2

6 years ago

2.2.1

6 years ago

2.2.0

6 years ago

2.1.2

6 years ago

2.1.1

6 years ago

2.1.0

6 years ago

2.0.0

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago

0.28.6

6 years ago

0.28.5

6 years ago

0.28.4

6 years ago

0.28.3

6 years ago

0.28.2

6 years ago

0.28.1

6 years ago

0.28.0

6 years ago

0.27.0

6 years ago

0.26.0

6 years ago

0.25.0

6 years ago

0.24.2

7 years ago

0.24.1

7 years ago

0.24.0

7 years ago

0.23.7

7 years ago

0.23.5

7 years ago

0.23.4

7 years ago

0.23.3

7 years ago

0.23.2

7 years ago

0.23.1

7 years ago

0.23.0

7 years ago

0.22.0

7 years ago

0.21.0

7 years ago

0.20.5

7 years ago

0.20.4

7 years ago

0.20.3

7 years ago

0.20.2

7 years ago

0.20.1

7 years ago

0.20.0

7 years ago

0.19.8

7 years ago

0.19.6

7 years ago

0.19.5

7 years ago

0.19.4

7 years ago

0.19.3

7 years ago

0.19.2

7 years ago

0.19.1

7 years ago

0.19.0

7 years ago

0.18.0

7 years ago

0.17.0

7 years ago

0.16.3

7 years ago

0.16.2

7 years ago

0.16.1

7 years ago

0.16.0

7 years ago

0.15.5

7 years ago

0.15.4

7 years ago

0.15.3

7 years ago

0.15.2

7 years ago

0.15.1

7 years ago

0.15.0

7 years ago

0.14.3

7 years ago

0.14.1

7 years ago

0.14.0

7 years ago

0.13.2

7 years ago

0.13.1

7 years ago

0.13.0

7 years ago

0.12.0

7 years ago

0.11.0

7 years ago

0.10.0

7 years ago

0.9.0

7 years ago

0.8.0

7 years ago

0.7.1

7 years ago

0.7.0

7 years ago

0.6.3

8 years ago

0.6.2

8 years ago

0.6.1

8 years ago

0.6.0

8 years ago

0.5.1

8 years ago

0.5.0

8 years ago

0.4.0

8 years ago

0.3.1

8 years ago

0.3.0

8 years ago

0.2.5

8 years ago

0.2.4

8 years ago

0.2.3

8 years ago

0.2.2

8 years ago

0.2.1

8 years ago

0.2.0

8 years ago

0.1.0

8 years ago