このブログははてなブログからの移行記事です。

npm run hogeでエラーを出したくない

eslintでのチェックやトランスパイルの実行は下のような感じでpackage.jsonに書いてnpm run lint等で実行するようにしてる。

{
  "scripts": [
    "lint": "eslint src/",
    "build": "babel src --out-dir dest"
  }
}

その際、eslint実行等の場合はコマンドの実行自体がコケるだけでnpmが鬱陶しいエラーを出してくる。

npm run lint実行結果

欲しいのはnpm scriptの結果で、下のはnpm初心者には無益で紛らわしいログでしかない

これをどうにかしたい

解決策を調べる

どうにかできないか調べてみた。

解決策その1:--silentオプションをつける

npmにはloglevelという概念があり、これにオプションを指定するとログの出力形式を変更できる。

docs.npmjs.com

その中の1つに--silentがあるのでnpm scriptを実行する際にこのオプションをつける。

省略形で-sでもよい。

--slientオプションつきnpm run lint

これで鬱陶しさはなくなったが

  • 毎回オプションをつけなければいけない
  • npm-debug.logを吐き出さないので本当にnpm由来のエラーが発生したらオプション外してもう一回実行しなきゃいけない

というデメリットがある

解決策その2:.npmrcを設定する

最近こんな記事を読んだ。

qiita.com

今まで知らなかったのが本当にもったいないくらい最高の話で、ローカルにnpmコマンドの設定を保持できる。

なので.npmrcを作成し、以下の用なオプションを指定すると常時、loglevelsilentになる

loglevel=silent

これで毎回オプションを足す手間は省ける。

ただし、これにもデメリットがあって

  • CI等でこうするとエラーが発生したときにログが読めない
  • npm-debug.logが吐き出されない

という問題が依然として残る

解決策その3:aliasでごまかす

後にも言うけどこの問題に根本的解決策は現状ないです。

なので上記デメリットを吸収できる方法は思いつく限りだとshellのaliadを指定する方法です。

要は単純で、alias名は何でもいいんだけど例えば下記のようなものを各々のdotfilesに追記する。

alias npm-run='npm run --silent $*'

これでnpm scriptsを実行したいときはnpm-run lintとかで実行する。

ログが欲しい時やCIではnpm run lintを使う。

解決策番外編:pipeで無理やり成功させる

これは全然解決策じゃなくて、絶対にやめたほうがよいのであえて書いた。

何かというと、npmのissueやstackoverflowを眺めてると「npm scriptに|| true足せばいいよ」ってのがあって

npm scriptの実行結果を無理やりtrueに持っていけばエラーじゃないからログも出ないぜという話。

{
  "scripts" [
    "lint": "eslint src/ || true"
  ]
}

ただ、確かにログは吐かなくなるんだけどnpm scriptの実行が失敗してもコマンドがこけないので

CI等で実行してる場合は例えばeslintが失敗してもそれを補足できなくなる。

絶対にやめような^^

結論

現状、「標準出力にいらんエラーを吐かせず、npm-debug.logはちゃんと残してくれるnpm scriptの書き方」はない。

ので以下の2策に逃げるしか無い気がする。

  • npm script実行時は-sオプションをつける
  • .npmrcloglevel=silentを指定する

ただし、前者は複数人開発だと周知が面倒だし後者はいざというときにログが無くて死ぬ可能性がある。

npmのissueにもこの話題はあがっていて、dev環境用のnpm scriptsを用意しようとかいろいろ提案されているみたい。

run-scripts are too noisy while used in development · Issue #8821 · npm/npm · GitHub

個人的にはnpm慣れてるので、個人ではshellのaliasで逃げつつチームの時はこのブログ記事ぶん投げようかなという感じ。

追記

匿名の方より以下のコメントをいただきました。

f:id:sota1235:20170127155929p:plain

これであれば「標準出力にいらんエラーを吐かせず、npm-debug.logはちゃんと残してくれるnpm scriptの書き方」を実現できそうです。

具体的にはこんな感じのshellを書いて使う。

#!/bin/zsh

###
# npm run with slim
###
DOTFILES_NPM_ERROR_LOG="$HOME/.dotfiles/dist/npm_error.log"

function npmrun() {
  npm run $1 2>$DOTFILES_NPM_ERROR_LOG

  if [ ! $? -eq 0 ]; then
    echo "npm error log recorded at $DOTFILES_NPM_ERROR_LOG"
    return 1
  fi
}

もしかしたらもっと良い書き方があるかもだが、これでひとまず解決した。最高!

COMMENT: コマンドラインだと、「npm run test 2>error.log」と手も。