JavaScriptNode.jsoclif

Nodeで作ったCLIツールににローディングや対話モデルを追加する

oclifでCLIツールをちょいちょい作るのですが、いろいろエフェクトや機能が欲しくなってきたので、後で見返すように覚え書き。 ローディングアニメーション的なものを入れる コマンド内でnpm installや非同期通信を […]

広告ここから
広告ここまで

oclifでCLIツールをちょいちょい作るのですが、いろいろエフェクトや機能が欲しくなってきたので、後で見返すように覚え書き。

ローディングアニメーション的なものを入れる

コマンド内でnpm installや非同期通信をさせてる場合、「あれ、ちゃんと動いてる?」となる時があります。ということで、待機中はローディング的なもの出しましょう。今回はcli-spinnerを使います。

$ npm i -S cli-spinner

ローディングの必要な処理を用意する

oclifだとasync / awaitで処理を書いていきます。なのでテストするときは、以下のようなsleepのプロミスを用意すると良いでしょう。

const sleep = sec => new Promise(resolve => setTimeout(resolve, sec))

処理はこんな感じで書けます。

const {Command} = require('@oclif/command')
const Spinner = require('cli-spinner').Spinner;
const sleep = sec => new Promise(resolve => setTimeout(resolve, sec))

class HelloCommand extends Command {
  async run() {
    const spinner = new Spinner('processing.. %s');
    spinner.setSpinnerString('|/-\\');
    spinner.start();
    // ここに非同期処理
    await sleep(1000)
    spinner.stop();
    // 結果を出す前に、改行を
    this.log(`\n`)
    this.log(`Finished`)
  }
}

実行すると、こんな感じになります。

$ ./bin/run hello
processing.. \
Finished

spinner.setSpinnerString()の引数文字列が順番に表示されます。

対話式にする

引数に必要なデータが無い場合やセットアップコマンドでは、再度コマンドを入力させるより対話式で確認した方が効率的です。ここからは、inquirerを使って実装してみます。

$ npm i -S inquirer

oclifの場合、flagにプロパティがあるかどうかを確認して、なければ選ばせるという方法を取ることができます。以下のようなヘルパーを書いておくと良いでしょう。

const inquirer = require('inquirer')
const getServiceName = async flags => {
  if (flags.name) return flags.name
  const responses = await inquirer.prompt([{
    name: 'name',
    message: 'select a service name',
    type: 'list',
    choices: [{name: 'AWS'}, {name: 'GCP'}, {name: 'Azure'}],
  }])
  return responses.name
}

あとはコマンドの処理部分に組み込みましょう。

class HelloCommand extends Command {
  async run() {
    const {flags} = this.parse(HelloCommand)
    const name = await getServiceName(flags)
    this.log(`You choose ${name}!`)
  }
}

こうすることで、以下のように対話式でデータを入力させることができます。

$ ./bin/run hello
? select a service name (Use arrow keys)
❯ AWS 
  GCP 
  Azure 
You choose AWS!

$ ./bin/run hello --name GCP
You choose GCP!

テキスト入力にすることや、複数項目にすることも可能です。

  const responses = await inquirer.prompt([{
    name: 'name',
    message: 'put a service name',
    type: 'input',
  }, {
    name: 'comment',
    message: 'put a comment',
    type: 'input',
  }])

こんな感じですね。

$ ./bin/run hello
? put a service name Amazon Web Service
? put a comment 
You choose Amazon Web Service!

OSで通知を出す

node-notifierでデスクトップ通知を出すこともできます。

$ npm i -S node-notifier

const {Command} = require('@oclif/command')
const Spinner = require('cli-spinner').Spinner;
const notifier = require('node-notifier')
const sleep = sec => new Promise(resolve => setTimeout(resolve, sec))

class HelloCommand extends Command {
  async run() {
    const spinner = new Spinner('processing.. %s');
    spinner.setSpinnerString('|/-\\');
    spinner.start();
    // ここに非同期処理
    await sleep(1000)
    spinner.stop();
    // 結果を出す前に、改行を
    this.log(`\n`)
    this.log(`Finished`)
    notifier.notify({
      title: 'My CLI Example',
      message: 'Finished'
    })
  }
}

これで処理が完了すると通知が出るようになります。

オプションで通知音の有無やアイコンのカスタマイズもできます。

    notifier.notify({
      title: 'My CLI Example',
      message: 'Finished',
      icon: path.join(__dirname, 'hello.png'),
      sound: true,
      wait: true
    })

Happy Node.js CLI Life !

ここにchalkを加えると、かなりいい感じのCLIツールっぽいものが作れる(はず)です。

最近作り始めたばかりですが、よくやるタスクをまとめていくと結構楽かつ楽しいので皆さんも是非。

ブックマークや限定記事(予定)など

WP Kyotoサポーター募集中

WordPressやフロントエンドアプリのホスティング、Algolia・AWSなどのサービス利用料を支援する「WP Kyotoサポーター」を募集しています。
月額または年額の有料プランを契約すると、ブックマーク機能などのサポーター限定機能がご利用いただけます。

14日間のトライアルも用意しておりますので、「このサイトよく見るな」という方はぜひご検討ください。

広告ここから
広告ここまで

Related Category posts