oclifでサブコマンドありのCLIコマンドを作る
やりたいこと aws cloudfront list-distributionsみたいな、サブコマンドありのコマンドを作りたい oclifでサブコマンドありのプロジェクトをセットアップする oclifでプロジェクトを作り […]
目次
やりたいこと
aws cloudfront list-distributions
みたいな、サブコマンドありのコマンドを作りたい
oclifでサブコマンドありのプロジェクトをセットアップする
oclifでプロジェクトを作ります。
$ npx oclif multi test
npx: 429個のパッケージを28.809秒でインストールしました。
(node:31637) ExperimentalWarning: The fs.promises API is experimental
_-----_ ╭──────────────────────────╮
| | │ Time to build a │
|--(o)--| │ single-command CLI with │
`---------´ │ oclif! Version: 1.11.4 │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? npm package name test
? command bin name the CLI will export test
? description test cli
? author hideokamoto @hideokamoto
? version 0.1.0
? license (MIT)
? node version supported >=8.0.0
? github owner of repository (https://github.com/OWNER/repo) hideokamoto
? github name of repository (https://github.com/owner/REPO) test
? optional components to include (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ yarn (npm alternative)
◉ mocha (testing framework)
◉ circleci (continuous integration/delivery service)
◉ appveyor (continuous integration/delivery service)
◉ codecov (online code coverage report viewer)
◉ typescript (static typing for javascript)
◉ tslint (static analysis tool for typescript)
(Move up and down to reveal more choices)
ディレクトリ構成
全部盛りだとこんな感じ。
$ tree -L 2 -I node_modules
.
├── README.md
├── appveyor.yml
├── bin
│ ├── run
│ └── run.cmd
├── lib
│ ├── commands
│ ├── index.d.ts
│ └── index.js
├── package.json
├── src
│ ├── commands
│ └── index.ts
├── test
│ ├── commands
│ ├── helpers
│ ├── mocha.opts
│ └── tsconfig.json
├── tsconfig.json
├── tslint.json
└── yarn.lock
実行するとこんな感じに。
$ ./bin/run -h
VERSION
test/0.0.0 darwin-x64 node-v10.1.0
USAGE
$ test [COMMAND]
COMMANDS
hello describe the command here
help display help for test
$ ./bin/run hello
hello world from ./src/commands/hello.ts
コマンド出力をカスタマイズする
デフォルトで生成されているhello
コマンドの出力を変えてみましょう。ファイルはsrc/commands/hello.ts
にあります。
import {Command, flags} from '@oclif/command'
export default class Hello extends Command {
static description = 'describe the command here'
static examples = [
`$ test hello
hello world from ./src/hello.ts!
`,
]
static flags = {
help: flags.help({char: 'h'}),
// flag with a value (-n, --name=VALUE)
name: flags.string({char: 'n', description: 'name to print'}),
// flag with no value (-f, --force)
force: flags.boolean({char: 'f'}),
}
static args = [{name: 'file'}]
async run() {
const {args, flags} = this.parse(Hello)
const name = flags.name || 'world'
this.log(`hello ${name} from ./src/commands/hello.ts`)
if (args.file && flags.force) {
this.log(`you input --force and --file: ${args.file}`)
}
}
}
オプションをカスタマイズする
static flags
で定義されている内容が、オプションとして利用できます。サンプルではname
/help
/force
が用意されており、-h
してみるとOPTIONS
にきちんと出てきます。
$ ./bin/run hello -h
describe the command here
USAGE
$ test hello [FILE]
OPTIONS
-f, --force
-h, --help show CLI help
-n, --name=name name to print
EXAMPLE
$ test hello
hello world from ./src/hello.ts!
ここに--dry-run
/-d
を追加したい場合、以下のようにすればOKです。
static flags = {
help: flags.help({char: 'h'}),
// flag with a value (-n, --name=VALUE)
name: flags.string({char: 'n', description: 'name to print'}),
// flag with no value (-f, --force)
force: flags.boolean({char: 'f'}),
'dry-run': flags.boolean({char: 'd', description: 'dry run mode'})
}
async run()
では以下のように変更すればOKです。
async run() {
const {args, flags} = this.parse(Hello)
const name = flags.name || 'world'
this.log(`hello ${name} from ./src/commands/hello.ts`)
// flags[FLAG_NAME]で判別できる
if (flags['dry-run']) {
// ここにdry-run時の処理を書く
this.log('dry run mode')
}
if (args.file && flags.force) {
this.log(`you input --force and --file: ${args.file}`)
}
}
追加されました。
./bin/run hello -h
describe the command here
USAGE
$ test hello [FILE]
OPTIONS
-d, --dry-run dry run mode
-f, --force
-h, --help show CLI help
-n, --name=name name to print
EXAMPLE
$ test hello
hello world from ./src/hello.ts!
$ ./bin/run hello -d
hello world from ./src/commands/hello.ts
dry run mode
サブコマンドを追加する
次は一番やりたかったサブコマンドの追加をやりましょう。aws cloudfront list-distributions
のlist-distributions
部分ですね。これはstatic args
を見ればOKです。
static args = [{name: 'file'}]
ここを以下のように変えてみましょう。
static args = [
{
name: 'action',
required: true,
description: 'list-distributions, get-distribution'
}
]
これだけで反映されます。
$ ./bin/run hello -h
describe the command here
USAGE
$ test hello ACTION
ARGUMENTS
ACTION list-distributions, get-distribution
required: true
とすることで、必須にできます。
$ ./bin/run hello
› Error: Missing 1 required arg:
› action list-distributions, get-distribution
› See more help with --help
ちなみに配列形式なので、複数登録できます。
// requiredじゃないものを追加する
static args = [
{
name: 'action',
required: true,
description: 'list-distributions, get-distribution'
}, {
name: 'file'
}
]
requiredかどうかで表記が変わります。
$ ./bin/run hello -h
describe the command here
USAGE
$ test hello ACTION [FILE]
ARGUMENTS
ACTION list-distributions, get-distribution
FILE
処理部分では以下のように書けばよさそうです。
const {args, flags} = this.parse(Hello)
switch (args.action) {
case 'list-distributions':
this.log('list distribution!')
break
case 'get-distribution':
this.log('get-distribution!')
break
default: {
this.error('uh oh!!!')
// exit with status code
this.exit(1)
}
}
許可してないサブコマンドがきた場合はthis.error
とthis.exit
で終了させましょう。
コマンドを追加する
サブではなくメインのコマンドも追加してみましょう。追加もコマンドから実行できます。
$ npx oclif command world
_-----_
| | ╭──────────────────────────╮
|--(o)--| │ Adding a command to test │
`---------´ │ Version: 1.11.4 │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
create src/commands/world.ts
create test/commands/world.test.ts
replacing <!-- usage --> in README.md
replacing <!-- commands --> in README.md
replacing <!-- toc --> in README.md
$ ./bin/run world -h
describe the command here
USAGE
$ test world [FILE]
OPTIONS
-f, --force
-h, --help show CLI help
-n, --name=name name to print
簡単ですね。