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ツールっぽいものが作れる(はず)です。

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

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark