Node.jsで外部コマンドを実行中に、ログを色付きで標準(エラー)出力

# はじめに
Node.jsのCLIツールを作っております。
そのツール内で外部コマンドとして`yarn install`を叩くんですが、
実行中のログに色とか絵文字がつかず、プレーンテキストで出て来ちゃったので、
綺麗に出力する方法を調べたのでメモです。

# 教科書通りにやると殺風景に
Nodeから外部コマンド実行は初めてだったので、
こちらの`child_process`のドキュメントと睨めっこしながら色々調べました。
https://nodejs.org/api/child_process.html

[`child_process.exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)だと「実行が終わってから何かする」って感じだったので、インストール状況を逐次出力は無理だなーと思い、
[`child_process.spawn()`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options)を使ってこうしてみました。

```js
const path = require('path')
const { spawn } = require('child_process')

const dirPath = path.resolve('project-dir')
const yarnInstall = spawn('yarn', ['install'], { cwd: dirPath })

// 実行中に標準エラー出力・標準出力を随時画面に出していく
yarnInstall.stderr.on('data', data => {
  console.error(data.toString())
})
yarnInstall.stdout.on('data', data => {
  console.log(data.toString())
})

// 終了時の処理
yarnInstall.on('close', code => {
  if (code !== 0) {
    console.error(`yarn process exited with code ${code}`)
  }
  console.log('`yarn install`:  done!')
})
```

結果こちら。
![スクリーンショット 2018-05-23 23.35.43.png](https://qiita-image-store.s3.amazonaws.com/0/24333/d0816b0f-64bd-1dbf-0d97-a29bd0535b65.png)
うーん、、、殺風景。
せっかく綺麗に出してくれるyarnの出力が真っ白になっておる :cry: 

# 色・絵文字を再現するには
オプションに`stdio: 'inherit'`を加えてやって、
自前のログ出力処理を外しちゃえばOK！

```js
const path = require('path')
const { spawn } = require('child_process')

const dirPath = path.resolve('project-dir')
// 標準入出力はすべて右から左へ受け流す
const yarnInstall = spawn('yarn', ['install'], { cwd: dirPath, stdio: 'inherit' })

// 終了時の処理
yarnInstall.on('close', code => {
  if (code !== 0) {
    console.error(`yarn process exited with code ${code}`)
  }
  console.log('`yarn install`:  done!')
})
```
結果はコチラ。`yarn install`の結果そのまんまですね！美しい！
余計な空行も消えてます。

![スクリーンショット 2018-05-23 23.35.59.png](https://qiita-image-store.s3.amazonaws.com/0/24333/6d8ee89c-36f3-6170-7cba-ee28052ac887.png)

## もう少し詳しく
`options.stdio`の公式docはコチラ。
https://nodejs.org/api/child_process.html#child_process_options_stdio

`stdio: 'inherit'`が「色・絵文字を再現」という表現は正確ではなくて、
「標準入出力を`spawn`ではハンドリングせず、そのまんまスルーしてターミナルに出す」
って感じのようです。

その証拠に、`stdio: 'inherit'`を付けて実行中は`stderr`などが`null`になっちゃうので、
`stderr.on()`とか書くとエラーになるのですねぇ。
エラー出てるのに標準出力はちゃんとされるので、最初意味わからずハマりましたw

```js
const yarnInstall = spawn('yarn', ['install'], { cwd: dirPath, stdio: 'inherit' })
yarnInstall.stderr.on('data', data => {
  console.error(data.toString())
})
```

```shell-session
yarnInstall.stderr.on('data', data => {
                   ^

TypeError: Cannot read property 'on' of null
    at Object.<anonymous> (/hoge/fuga/exec-yarn-install.js:6:20)
    at Module._compile (internal/modules/cjs/loader.js:678:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10)
    at Module.load (internal/modules/cjs/loader.js:589:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
    at Function.Module._load (internal/modules/cjs/loader.js:520:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
    at startup (internal/bootstrap/node.js:228:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:576:3)
```

![スクリーンショット 2018-05-23 23.53.09.png](https://qiita-image-store.s3.amazonaws.com/0/24333/be4be216-36bc-1b26-877e-55cc81711608.png)

# 参考サイト
[child_process and color : node - Reddit](https://www.reddit.com/r/node/comments/5zfhkj/child_process_and_color/)
[node.js - preserve color when executing child_process.spawn - Stack Overflow](https://stackoverflow.com/questions/7725809/preserve-color-when-executing-child-process-spawn)
[javascript - Retaining output colors when shelling out to node - Stack Overflow](https://stackoverflow.com/questions/18825493/retaining-output-colors-when-shelling-out-to-node)
[Node.jsで子プロセスを起動し、非同期的に実行する](http://info-i.net/child-process-spawn)
[Node.jsで子プロセスの標準入出力を処理する](http://info-i.net/child-process-spawn-stdio)
[右から左へ受け流す](https://ja.wikipedia.org/wiki/%E3%83%A0%E3%83%BC%E3%83%87%E3%82%A3%E5%8B%9D%E5%B1%B1)

日本語の情報がなかなか少なかったです。
今後は、僕のこの記事でサクッと解決ですねっ:mag: :truck: :link: :page_with_curl: :sparkles: