npm-scripts でチームメンバーと共通認識を作る

プロジェクト固有の処理は npm-scripts に記載して、メンバーが必要なときに呼び出せるようにしておきましょう。 npm-scripts の解説から便利な使用方法までをまとめます。
2020.05.13

npm-scripts とは package.json の scripts プロパティで定義する、一連の便利処理です。本記事では、 npm-scripts を使った開発効率の加速と、プロジェクトにジョインしたらまず npm-scripts を把握すべき理由、最後に npm-scripts を補完する方法を紹介します。

この記事は CX 事業本部の Tech Lead のお仕事紹介第 5 弾です。

TL;DR

  • test スクリプトは必ず定義しよう
  • start スクリプトは定義できる場合は必ず定義しよう
  • スクリプトにはわかりやすい名前をつけよう
  • プロジェクトでの作業はできるだけスクリプトを使おう
  • ソースコードはスクリプトから追っていこう
  • 楽するためにスクリプト名補完の設定をしよう

npm-scripts とは ?

npm run-script <スクリプト名> と打つことで、 <スクリプト名> に指定したスクリプトを実行可能な npm の仕組みです。 run-script サブコマンドは少々長いので、 run エイリアスが用意されています。これによって npm run <スクリプト名> と打つことが可能になります。実際の使用は run サブコマンドを使うことが普通です。

<スクリプト名> にあたる部分は package.json の scripts プロパティで定義します。

{
  "scripts": {
    "myFirstScript": "echo done"
  },
}

これは myFirstScript というスクリプトを定義している例です。 npm run myFirstScript を実行可能となり、実行結果として done という文字列が表示されます。

$ npm run myFirstScript

> scripts@1.0.0 myFirstScript /path/to/your/project
> echo done

done

特別扱いのスクリプト名

次の名前を持つスクリプトは特別扱いです。

  • test
  • start

これらは run サブコマンドを指定する必要がなく、それぞれ npm test および npm start で実行可能です。また、 testt および tst というエイリアスを持っているため、 npm t もしくは npm tst で実行可能です。

test はテストを実行する意味合いをもっているスクリプト名です。使い捨てのプロジェクトを除き、必ず定義しましょう。プロジェクトへのジョイン時や久しぶりにコードを触る際にまずテスト実行し、プロジェクト把握や変更の足がかりとすると良いでしょう。

start はそのプロジェクトで提供すべきものを実行するためのスクリプト名です。たとえばサーバー用のプロジェクトであればサーバーを立ち上げるための定義を、コマンドラインを提供するプロジェクトであればそのコマンドラインを実行するための定義を書きましょう。フロントエンド開発プロジェクトではバンドル用のサーバーを立ち上げるなどの用途で利用されます。関数のみを提供するなど、ライブラリーとして提供される目的のプロジェクトでは定義しなくとも構いません。

prepost

スクリプト名の前に pre をつけたものは該当スクリプトの実行直前に実行されます。また、該当スクリプトの実行直後に実行される post プリフィクスも存在しています。 testpretestposttestが定義されている際に test を実行すると次の順番で実行されます。

  1. pretest
  2. test
  3. posttest

それぞれ前準備と後処理を定義したい場合に使いましょう。

なお、 preinstallpostinstall を定義すると、パッケージがインストールされる直前と直後に実行する処理を指定可能です。セキュリティを確保する目的使用が推奨されていませんが、プロジェクトメンバーのみの参照に限定されているプロジェクトで、使用者の手元でビルドが必要なパッケージを作成する際などに使用しましょう。

同様に prepublish という publish 前に実行する処理も定義可能ですが、歴史的経緯によって使用を一時的に控える必要があります。現在、 prepublish 相当のことを定義したい場合は代わりに prepublishOnly を定義しましょう。遠くない未来に prepublishOnly が deprecated になったという警告メッセージが表示されるようになりますので、その際に prepublishOnlyprepublish へ変更しましょう。

その他あると便利なスクリプト

次のスクリプトを定義しておくと便利でしょう。

スクリプト名 目的
build ビルド
clean ビルド結果の削除
type-check 型チェック
lint 静的コード解析
coverage コードカバレッジ計測

型チェックや静的コード解析については、普段エディターや IDE で自動的に実行していると思いますが、 CI 用に定義しておくと便利です。また、これらが明示的に定義してあることでジョインしたばかりのプロジェクトメンバーがそれぞれの目的でどのツールを使うかを把握することが可能です。

スクリプト名の修飾

例えば development 環境と production 環境でビルド用の設定が異なる場合など、実行すべきスクリプトを変更したいときがあります。これは慣習的に : コロンでつないで環境名を指定することが多いです。例えば普段使う build コマンドは development 環境用とし、 build:prod コマンドに production 環境用のスクリプトを定義する、などです。

yarn における scripts プロパティの扱い

yarn も同様の仕組みを持っておりyarn run <スクリプト名> と打つことで実行可能です。

ただし、 yarn では run サブコマンドを省略可能です。次のコマンド群はまったく同じ動作となります。

  • yarn run myFirstScript
  • yarn myFirstScript

また、 test スクリプトのエイリアスは特に設定されていません。そのため yarn t は実行できず、 yarn test と打つ必要があります。

$ yarn myFirstScript
yarn run v1.22.4
$ echo done
done
✨  Done in 0.04s.

yarn でスクリプトを実行した場合、最後に実行時間が表示されます。

package.json 例

ここで一度 package.json の例を挙げてみます。scripts プロパティ以外は省略しています。

{
  "scripts": {
    "start": "webpack-dev-server --config webpack.config.wds.js",
    "build": "webpack --config webpack.config.dev.js",
    "build:prod": "webpack --config webpack.config.prod.js",
    "type-check": "tsc --noEmit",
    "lint": "eslint --ext '.ts' --ext '.tsx' .",
    "test": "jest",
    "coverage": "jest --coverage",
  }
}

webpack を用いたフロントエンドプロジェクトの例です。 starttest の他、環境別の build やその他チェック用のスクリプトを定義しています。

CI 設定で type-check / lint / coverage などを用いるとよいですね。

スクリプトからプロジェクトの構造を追う

さて、上述した package.json を持つプロジェクトにジョインした方がどのようにプロジェクトを把握するかを追ってみましょう。

まず依存パッケージの挙動が変わることによって壊れていないかを確かめようとするでしょう。その際は scripts プロパティを眺め、 test スクリプトが定義されていることを発見し、実行するでしょう。 Node.js プロジェクトに慣れている方であればスクリプトの定義を確認せずに npm tyarn test をまず実行するかもしれません。

ここで、 test には Jest を使っていることがわかるため、 jest.config.js や package.json の jest プロパティを参照してテストに関する設定を確認することが可能です。また、各テストケースを確認するためには __tests__ ディレクトリーや .test.ts 拡張子がついたファイルを探せばよいこともわかりますね。

同様にプロジェクトを動かしたいと思った際も start スクリプトを探して実行するという流れとなります。もし start スクリプトが定義されていない場合、そのプロジェクトでは何も実行するものがない、と解釈されるでしょう。

上述の例では、 start スクリプトは webpack-dev-server を用いてフロントエンド開発用のサーバーを立ち上げる定義となっています。こちらは指定されている設定ファイルを参照し、エントリーポイントからコードをたどっていくことが可能です。

複数行のスクリプトを使いたい場合

少し複雑な処理など、 1 行での定義が難しい場合は別途シェルスクリプトや JavaScript で処理を書き下すなどし、それを各種 npm-scripts に指定しましょう。

bin ディレクトリーは実行可能なパッケージを作成する際に使うものという慣習があるため、 utilsscripts ディレクトリーなどに格納しましょう。

/
  scripts/
    awesome.sh
  package.json

上述のようなファイルツリーの構造とし、 package.json の中身は次としましょう。

{
  "scripts": {
    "awesome": "./scripts/awesome.sh"
  }
}

スクリプト名の補完

デフォルトではスクリプト名は補完対象になっていません。これではいちいち package.json の中身を覗いて使用可能なものを確認する必要があるだけでなく、スクリプト名をすべて打たなければならないので面倒くさいです。これを補完可能とする設定を紹介します。

npm では npm completion サブコマンドの実行結果をお使いのシェルの設定ファイルに書き込むことで補完が効くようになります。スクリプトだけでなくサブコマンドの補完も効くようになるため、ぜひ設定しましょう。

ただし、この設定は補完時に npm completion サブコマンドを呼び出しているせいか一瞬の待ちが発生します。この待ちが嫌な方は自分で補完用の設定を書いてみましょう。手元では jq コマンドで package.json をパースすることで素早く補完されるようになりました。なお、この設定例は zsh 用です。 bash など他のシェルをお使いの方は参考にして自分で書いてみてください。

他にも peco を使って補完する方法で快適な環境を作成している方もいらっしゃいます。 bash での設定例なので普段遣いされている方にはとても参考になる記事です。

yarn ではすでに有志の方による zsh 用補完関数が提供されていたのでこちらを利用しました。 bash その他のシェルの情報をお持ちの方は教えて下さい。

まとめ

npm-scripts の定義方法から実際の使い方までを紹介しました。普段プロジェクトをメンテナンスされている方は意識して各スクリプトを整備してみましょう。また、今後新しいプロジェクトにジョインする際は本記事で紹介した方法を使ってプロジェクト構造を把握することに挑戦してみてください。

なお、 npm-scripts ですべての齟齬が解消されるわけではありません。適宜 README に補足事項を書いたり、話し合って積極的に共通認識の形成を図りましょう。本記事はそのための足がかりとして使っていただければ本望です。