Ruby
Rails
Node.js
yeoman

【Node.js】Yeomanのカスタムジェネレータを使って綺麗にログ出力

はじめに

Yeomanのジェネレータとして、Goqoo on kintoneというScaffoldツールを作っております。

yeoman-generator内でthis.fs.copyTpl()とかでファイルをコピーすると、
Railsのジェネレータと同様、こんなカラフルなログを出してくれるのがとても良いですよね。
スクリーンショット 2018-10-11 11.26.58.png

こんなログを、自分の好きなタイミングで自由自在に出したいという話。

用途

this.spawnCommandSync()で子プロセスを走らせた時に、Railsのrun git init run bundle installみたいなログを出したかったんです。(Yeomanの標準機能で欲しいところですけどね)
スクリーンショット 2018-10-11 11.47.03.png

あとは、エラーメッセージもカラフルに出したいとか、色々あります。

やり方

基本:this.log配下の関数を使い分ける

ジェネレータ内でログ出力するときはthis.log()を使うことが多いと思いますが、実はその配下に、this.log.xxx()という関数がステータス別に沢山用意されています。

generators/app/index.js
const Generator = require('yeoman-generator')

module.exports = class extends Generator {
  configuring() {
    const str = 'Yo! ora goqoo!'
    this.log(str)
    this.log.write(str)
    this.log.writeln(str)
    this.log.ok(str)
    this.log.error(str)
    this.log.skip(str)
    this.log.force(str)
    this.log.create(str)
    this.log.invoke(str)
    this.log.conflict(str)
    this.log.identical(str)
    this.log.info(str)

    // ...do something
  }
}

結果はこんな感じ。綺麗ですね!
デフォルトで用意されてるステータスで事足りるなら、これで十分です。
スクリーンショット 2018-10-11 11.40.00.png

ちなみに公式ドキュメントの解説はこの一言だけ!
「ソース読め!」としか書いてないスパルタ感 :joy:

Adapter#log()
It’s both a function and an object intended for generic output. See lib/util/log.js for the complete list of methods to provide.

応用:this.logにステータスを追加

最初に書いたrunというステータスは、残念ながら用意されていません。
しかし!カスタムステータスを定義する方法があります。

https://github.com/yeoman/environment/blob/v2.3.3/lib/util/log.js#L46-L51
この関数で生成されるのがthis.logの正体ですが、引数に以下のようなオブジェクトを渡して呼び出せばステータス&カラーを追加できるんですね。

{
  colors: {
     status: color,
  }
}

これをrequire('yeoman-environment/lib/util/log')とすることで、単独で使うことができます!

generators/app/index.js
const Generator = require('yeoman-generator')
const EnvironmentLogger = require('yeoman-environment/lib/util/log')

module.exports = class extends Generator {
  constructor(args, options) {
    // 親コンストラクタを実行
    super(args, options)
    // カスタムステータス付きのlogオブジェクトを作ってオーバーライド
    this.log = EnvironmentLogger({
      colors: {
        run: 'green',
      },
    })
  }

  configuring() {
    this.log.skip('package.json')
    this.log.conflict('.eslintrc.yml')
    this.log.identical('.gitignore')
    this.log.run('git init')

    // ...do something
  }
}

もちろん、デフォルトのステータスも引き続き使うことができます。こんな感じ〜♪
スクリーンショット 2018-10-11 12.27.50.png

Yeoman以外のNode.jsアプリで使う

普通のNodeアプリ内で、このloggerを単独で使うことも可能です。
こんな風にカジュアルに使っちゃうのも良いかもしれませんねっ!

const log = require('yeoman-environment/lib/util/log')()
log.ok('完了です!')
log.error('エラーです!')

スクリーンショット 2018-10-11 12.56.38.png

ちなみにRailsの場合

調べていくうちにどんどん興味が深くなり、Railsの調査にまで手を出してしまいましたw

rails newrun bundle installみたいに出力してるのは、ここですね。
https://github.com/rails/rails/blob/v5.2.1/railties/lib/rails/generators/app_base.rb#L404

say_statusという関数で綺麗に出力できるようで、他にも至る所で使われています。
その定義はThorというライブラリ内にありました。色を指定しない場合はgreenになるようですね!
https://github.com/erikhuda/thor/blob/v0.20.0/lib/thor/shell/basic.rb#L101-L114

おわりに

この仕組みに辿り着くまでかなり苦労しました。。
日本語はおろか、英語の解説記事も全然なく、コードだけが頼りでした。

使い方さえ分かってしまえば至ってシンプルなので、Node.jsアプリでログを綺麗に、かつ苦労せず出したい皆さん、どんどん使ってみてくださいませ!