発端
npm@5 になるときに、 prepublish
の挙動が大きく変更されました。
Node.js の 8.x が LTS になった当時、Node.js@6 は npm@3 でしたが、Node.js@8 では npm@5 になるため、
その内容をちゃんと把握できていなかったので、調べました。
作業して結果をまとめたリポジトリがあるので、それの日本語訳+アルファ を書きます。
"ステップ4" と "ステップ 5" とはなにか
まとめを読むのに必要な用語(?)なので、先に説明しておきます。
https://github.com/npm/npm/issues/10074 で言ってるやつです。
ステップ 4 は、
In a year or so, make a semver-major bump to npm and make prepublish's behavior match prepublishOnly.
1年後くらいに、メジャーバージョンを上げて(=破壊的変更だと明示して)、prepublish
をprepublishOnly
と同じ挙動になるに変更した状態にします。
ステップ 5 は、
Either then or sometime after that, deprecate
prepublishOnly
and have justprepare
andprepublish
.
適当に時間が経った後、prepublishOnly
を非推奨にして(≒削除)、prepare
とprepublish
だけがある状態にします。
今現在(2018-02-07)は ステップ 3 まで実施済みの状態です。
TL;DR: 今は prepublish
は避け、 prepare
と prepublishOnly
にしよう
現状は、まだ「移行作業を進めるための段階」です。 prepare
はまだ"本来の挙動" ではないので、使用は控えるべきです。もともと prepare
で行っていたタスクは、きちんと prepublisOnly
と prepare
のどちらか適切な方に振り分け、「 prepare
ではなにもしていない状態」にして、次の step 4 に備えましょう。
step 4 が来て prepublish
が本来の姿になったら 、速やかに prepublishOnly
のものを prepublish
へ戻して「 prepublishOnly
を使用していない状態」にし、 step 5 へ備えましょう。
調査内容
作業したリポジトリ(どこで、なにを、どうやってやったか)
prepublish と prepare の(現状のパット見よくわからない状態になった)発端的なものは、 https://docs.npmjs.com/misc/scripts#prepublish-and-prepare とか https://github.com/npm/npm/issues/10074 を見てください。
.travis.yml を見ればわかりますが、複数のバージョンで npm install
と npm install ./local_module/sub_project
と npm publish
と npm pack
を実行してます。
どの npm-script がどのような順番で実行されたか?を眺めて、なんとなく表にしてみました。
実行結果は、 https://travis-ci.org/ndxbn/npm_prepbulish_migration_test で見れます。
エラーになってるやつは、 npm publish
を private: true
で抑止してるからなので、意図通りです。
使用する package.json の概要
必要なところは、以下の通りです。
{
"name": "npm_prepbulish_migration_test",
"private": true,
"dependencies": {
"npm_prepbulish_migration_test_sub": "file:local_module/sub_project"
}
}
この後の表に出てくる、 "main" というのは npm_prepbulish_migration_test
(以下、メインモジュール側)、"sub" というのは local_module/sub_project
のこと(以下、サブモジュール側)です。
やってみた結果
表の数字は、実行された順番です。
npm install
したときに実行される npm-script の移り変わり
npm script stage \ version | 2.14.3 | 2.15.11 | 3.8.6 | 3.10.10 | 4.2.0 | 5.6.0 | step 4 | step 5 |
---|---|---|---|---|---|---|---|---|
prepublish main | 8 | 8 | 8 | 8 | 9 | 7 | No | No |
preinstall main | 1 | 1 | 5 | 2 | 3 | 1 | 1 | 1 |
install main | 6 | 6 | 6 | 6 | 7 | 5 | 5 | 5 |
postinstall main | 7 | 7 | 7 | 7 | 8 | 6 | 6 | 6 |
prepare main | No | No | No | No | 10 | 8 | 7 | 7 |
=== | == | == | == | == | == | == | == | == |
prepublish sub | 2 | 2 | 1 | 1 | 1 | No | No | No |
preinstall sub | 3 | 3 | 2 | 3 | 4 | 2 | 2 | 2 |
install sub | 4 | 4 | 3 | 4 | 5 | 3 | 3 | 3 |
postinstall sub | 5 | 5 | 4 | 5 | 6 | 4 | 4 | 4 |
prepare sub | No | No | No | No | 2 | No | No | No |
見どころは、
- 4.2.0 で
prepare
が追加され、実行されるようになった - 5.6.0 で サブモジュール側で
prepublish
されなくなった - 5.6.0 で サブモジュール側で
prepare
されなくなった- たぶん、「そもそもサブモジュール側でインストール時に
prepare
するのおかしくね?」ということに気づいた
- たぶん、「そもそもサブモジュール側でインストール時に
- step 4 で メインモジュール側でも
prepublish
が走らなくなる
あたりです。
npm install
した時に サブモジュール側が prepare
されないのは、 npm publish
するときに prepare
された結果のものだけを npm registry にアップロードするため、インストール時にインストールしたものを prepare
する必要がないからだと思います。
node_modules にインストールし終わったあとに、 main のほうで prepare
が実行されるのは、 prepublish
が実行されていたのと同様に「便利だから」だと思います。イマドキの node module はなにかしらの ”ビルドプロセス" があるでしょう。
4.2.0 以前では、サブモジュール側でも prepublish
されていたようですが、(それに引きづられて prepare
も実行されるようになっていましたが、)こうなった経緯と「これそもそもおかしくね?」という議論までは確認できていません。ごめんなさい。
npm install foo
したときに実行される npm-script の移り変わり
npm script stage \ version | 2.14.3 | 2.15.11 | 3.8.6 | 3.10.10 | 4.2.0 | 5.6.0 | step 4 | step 5 |
---|---|---|---|---|---|---|---|---|
main module のものは、なにも実行されない | -- | -- | -- | -- | -- | -- | -- | -- |
=== | == | == | == | == | == | == | == | == |
prepublish sub | 1 | 1 | 1 | 1 | 1 | No | No | No |
preinstall sub | 2 | 2 | 2 | 2 | 3 | 1 | 1 | 1 |
install sub | 3 | 3 | 3 | 3 | 4 | 2 | 2 | 2 |
postinstall sub | 4 | 4 | 4 | 4 | 5 | 3 | 3 | 3 |
prepare sub | No | No | No | No | 2 | No | No | No |
見どころは、
- 4.2.0 で
prepare
が追加され、実行されるようになった - 5.6.0 で サブモジュール側で
prepublish
されなくなった - 5.6.0 で サブモジュール側で
prepare
されなくなった
あたりです。
prepare
されなくなった理由は、『 npm install
したときに実行される npm-script の移り変わり』に書いたのと同じ理由だと思います。
npm publish
したときに実行される npm-script の移り変わり
npm script stage \ version | 2.14.3 | 2.15.11 | 3.8.6 | 3.10.10 | 4.2.0 | 5.6.0 | step 4 | step 5 |
---|---|---|---|---|---|---|---|---|
prepublish main | 1 | 1 | 1 | 1 | 1 | 1 | 3 | 2 |
prepublishOnly main | No | No | No | No | 3 | 3 | 2 | No (deleted) |
publish main | 3 | 3 | 3 | 3 | 5 | 7 | 7 | 6 |
postpublish main | 4 | 4 | 4 | 4 | 6 | 8 | 8 | 7 |
prepack main | No | No | No | No | No | 4 | 4 | 3 |
pack main | No | No | No | No | No | No! | No? | No? |
postpack main | No | No | No | No | No | 5 | 5 | 4 |
prepare main | No | No | No | No | 2 | 2 | 1 | 1 |
(is_private) main | 2 | 2 | 2 | 2 | 4 | 6 | 6 | 5 |
=== | == | == | == | == | == | == | == | == |
サブモジュールのものは、なにも実行されない | -- | -- | -- | -- | -- | -- | -- | -- |
prepack
と postpack
は実装されたのが v5.0.0 です。
見どころは
- 5.0.0 から
prepack
とpostpack
が導入された(今回のはあまり関係ない) - 5.6.0 から step 4 になるときに、
prepublish
の実行タイミングが変更される - step 5 で、
prepublishOnly
が非推奨になる
あたりです。
「5.6.0 から step 4 になるときに、 prepublish
の実行タイミングが変更される」のは、
- In a year or so, make a semver-major bump to npm and make prepublish's behavior match prepublishOnly.
1年後くらいに、メジャーバージョンを上げて(=破壊的変更だと明示して)、prepublish
をprepublishOnly
と同じ挙動になるに変更した状態にします。
(再掲)
を根拠に言っています。
これを忠実にやるのであれば、
-
prepublish
はprepare
の後に実行されるように変更される -
prepublish
とprepublishOnly
はお互いの実行に依存していないはずだし、入れ替えても問題ないはず
の2点から、「step 4 では、 prepublish
が prepublishOnly
の後に実行される」ように変更されるのではないかと予想しています。
npm pack
したときに実行される npm-script の移り変わり
npm pack
というコマンドは、もしかしたら馴染みのない人が多いかもしれません。
ドキュメントはちゃんとありますし、 prepublish
のほうにも「(registry に アップロードしないという意味の)"dry run" がしたかったら、 npm pack
を使えばいいんじゃないかな」と下の方に書いてあります。
そんなコマンドでも、 prepublish
は実行されていました。
npm script stage \ version | 2.14.3 | 2.15.11 | 3.8.6 | 3.10.10 | 4.2.0 | 5.6.0 | step 4 | step 5 |
---|---|---|---|---|---|---|---|---|
prepublish main | 1 | 1 | 1 | 1 | 1 | 1 | No | No |
prepack main | No | No | No | No | No | 3 | 2 | 2 |
pack main | No | No | No | No | No | No! | No? | No? |
postpack main | No | No | No | No | No | 4 | 3 | 3 |
prepare main | No | No | No | No | 2 | 2 | 1 | 1 |
=== | == | == | == | == | == | == | == | == |
サブモジュールのものは、なにも実行されない | -- | -- | -- | -- | -- | -- | -- | -- |
見どころとしては、
- 4.2.0 で、pack する前に
prepare
されるようになった - step 4 で、
prepublish
されなくなる
あたりでしょうか。
iarna 氏の以下のコメントに説明されている意図通りになった、という感じです。
"I want to do build steps that are neccessary to use my module." — use prepare
prepare
は、「モジュールを実行できるようにするために、ビルドする」ステップであってほしい。
"I want to do validation steps that stop me from publishing bad code." — use prepublishOnly
("本来の"prepublish
という意味で)prepublishOnly
は、「おかしなコードを publish しないように、バリデーションするステップ」であってほしい。
npm pack
は、 「 npm publish
から ファイルのアップロードをする部分を抜いただけのもの」ではありません。単に「 npm publish
する時と同じ tar ball を作る」というだけなので、npm pack
では prepublish
は実行されるべきではありません。
まとめ
おまけ
prepublishOnly should run on npm pack
という ISSUE がありました。私も、最初はこの疑問を持ちましたが、やはり同じように疑問に思う人はいる(?)みたいですね。
npm publish
は、実際に publish して確認しました。ログは、GitHub のリポジトリに含めています。