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

# はじめに
[Yeoman](http://yeoman.io/)のジェネレータとして、`Goqoo on kintone`というScaffoldツールを作っております。

`yeoman-generator`内で`this.fs.copyTpl()`とかでファイルをコピーすると、
Railsのジェネレータと同様、こんなカラフルなログを出してくれるのがとても良いですよね。
<img width="339" alt="スクリーンショット 2018-10-11 11.26.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/6612134e-5b4b-2892-46d9-489e04841a1e.png">

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

# 用途

`this.spawnCommandSync()`で子プロセスを走らせた時に、Railsの`run git init` `run bundle install`みたいなログを出したかったんです。（Yeomanの標準機能で欲しいところですけどね）
<img width="505" alt="スクリーンショット 2018-10-11 11.47.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/701b4e01-2dee-1f19-5fea-fa08edb508d8.png">

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

# やり方
## 基本：this.log配下の関数を使い分ける

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


```js: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
  }
}
```
結果はこんな感じ。綺麗ですね！
デフォルトで用意されてるステータスで事足りるなら、これで十分です。
<img width="242" alt="スクリーンショット 2018-10-11 11.40.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/864728d4-d451-c6c6-06f5-946ef6f935cc.png">

ちなみに[公式ドキュメント](http://yeoman.io/authoring/integrating-yeoman.html)の解説はこの一言だけ！
「ソース読め！」としか書いてないスパルタ感 :joy:

> Adapter#log()
It’s both a function and an object intended for generic output. See [lib/util/log.js](https://github.com/yeoman/environment/blob/master/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`の正体ですが、引数に以下のようなオブジェクトを渡して呼び出せばステータス＆カラーを追加できるんですね。

```js
{
  colors: {
     status: color,
  }
}
```

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

```js: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
  }
}
```


もちろん、デフォルトのステータスも引き続き使うことができます。こんな感じ〜♪
<img width="258" alt="スクリーンショット 2018-10-11 12.27.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/4152a3f3-67fa-f161-f9ba-2988e7dd6f8e.png">

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

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

```js
const log = require('yeoman-environment/lib/util/log')()
log.ok('完了です！')
log.error('エラーです！')
```
<img width="140" alt="スクリーンショット 2018-10-11 12.56.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/3949ea2a-7663-ab2e-1fcc-7accd93e4ae6.png">

# ちなみにRailsの場合

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

`rails new`で`run 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アプリでログを綺麗に、かつ苦労せず出したい皆さん、どんどん使ってみてくださいませ！