blog.re-taro.dev
投稿日
更新日

Life Cycle Scripts を理解する

npm scripts には、prepost というプレフィックスのついたスクリプトがある。これは、スクリプトの前後に実行されるスクリプトを指定するものである。

その中でも、特定の状況下で実行されるスクリプトがある。それらを Life Cycle Scripts と呼ぶ。この挙動が混乱しがちなので、整理してみた。

Life Cycle Scripts とは、prepost のついたスクリプトの中でも、特定の状況下で実行されるスクリプトのことである。

具体的には、以下のようなスクリプトが Life Cycle Scripts に該当する。

npm installnpm ci が実行されたときに実行されるスクリプトである。

npm publish では実行されない。(ナント!!!)

このスクリプトは、名前と挙動の違いから混乱するという理由で非推奨1であり、代わりに prepare スクリプトと prepublishOnly スクリプトを使うことが推奨されている。prepublish のユースケースを公式では以下のように説明されている。

If you need to perform operations on your package before it is used, in a way that is not dependent on the operating system or architecture of the target system, use a prepublish script. This includes tasks such as:

  • Compiling CoffeeScript source code into JavaScript.

  • Creating minified versions of JavaScript source code.

  • Fetching remote resources that your package will use.

--- Prepare and Prepublish

prepare スクリプトは、npm installnpm publish , npm pack が実行されたときに、実行されるスクリプトである。

prepublish スクリプトの挙動の互換性の観点から、prepare スクリプトは prepublishOnly スクリプトの前に実行され、prepublish スクリプトの後に実行される。

prepublishOnly スクリプトは、npm publish が実行されたときにのみ、実行されるスクリプトである。(シンプル!)

tarball が作成される前に実行されるスクリプトである。tarball が作成されるタイミングは以下の通りである。

  • npm pack

  • npm publish

  • git URL でのインストール

ここで npm run packnpm pack は別である。前者はユーザーが定義したスクリプトであり、prepack は実行されない。

tarball が作成された後に実行されるスクリプトである。

node_modules ディレクトリが変更されたとき、変更の後に実行される。

次に npm のコマンドが実行されたときに、どの Life Cycle Scripts がどの順番で実行されるかを整理する。

コマンド実行される Life Cycle Scripts の種類と順番
npm cache addprepare
npm cipreinstall --> install --> postinstall --> prepublish --> preprepare --> prepare --> postprepare
npm diffprepare
npm install2preinstall --> install --> postinstall --> prepublish --> preprepare --> prepare --> postprepare
npm packprepack --> pack --> postpack
npm publish3prepublishOnly --> prepack --> prepare --> postpack --> publish --> postpublish
npm rebuild4preinstall --> install --> postinstall --> prepare
npm restartprerestart --> restart --> postrestart
npm startprestart --> start --> poststart
npm stopprestop --> stop --> poststop
npm testpretest --> test --> posttest
npm versionpreversion --> version --> postversion

余談だが npm 以外のパッケージマネージャでは、原則 prepost プレフィックスの付いたスクリプトは実行されない。これはワークフローの動作が複雑になることを避けるために設計されている。56

また yarn5 や pnpm7 では定義されている Life Cycle Scripts が npm と異なるので注意が必要である。


@s6n_jp から指摘されて気づいたのだが、どうやら pnpm v9 以降では Life Cycle Scripts がサポートされるようになった8ようだ。これにより、pnpm でも npm と同様に Life Cycle Scripts が実行されるようになった。

Twitter で poll を取った結果9入った変更のようで、多くの人がこの変更を望んでいたようだ。

調べていくと、npm の Life Cycle Scripts は意外と複雑で、挙動が混乱しやすい。しかし、理解してしまえば、それほど難しいものではない。この記事が、少しでもその理解に役立てば幸いである。


脚注

  1. binding.gyp というファイルが存在する場合、install スクリプトは node-gyp rebuild が代わりに実行される。

  2. --dry-run オプションを指定した場合、prepare は実行されない。

  3. prepare はリンクされたパッケージなど、カレントディレクトリがシンボリックリンクの場合にのみ実行される。