2025-05-12 追記:いつ prepublishOnly なくなんの?→未定(たぶんやらない)
Are changes to prepublishOnly and prepublish still planned? #8191 にある通りなんですけど、"ステップ4" と "ステップ5” はおそらく今後も実施されません。
https://github.com/npm/statusboard/issues/267 あたりでドキュメントに prepublishOnly も含めたライフサイクルが明記された上で、「Deprecation Note: prepublish」というセクションができているため、わざわざ削除するための作業をする必要性もなさそうに思います。
発端
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
prepublishOnlyand have justprepareandprepublish.
適当に時間が経った後、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 のリポジトリに含めています。