Edited at

npm の prepublish と prepare の変遷


発端

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年後くらいに、メジャーバージョンを上げて(=破壊的変更だと明示して)、 prepublishprepublishOnly と同じ挙動になるに変更した状態にします。


ステップ 5 は、


Either then or sometime after that, deprecate prepublishOnly and have just prepare and prepublish.

適当に時間が経った後、 prepublishOnly を非推奨にして(≒削除)、 prepareprepublish だけがある状態にします。


今現在(2018-02-07)は ステップ 3 まで実施済みの状態です。


TL;DR: 今は prepublish は避け、 prepareprepublishOnly にしよう

現状は、まだ「移行作業を進めるための段階」です。 prepare はまだ"本来の挙動" ではないので、使用は控えるべきです。もともと prepare で行っていたタスクは、きちんと prepublisOnlyprepare のどちらか適切な方に振り分け、「 prepare ではなにもしていない状態」にして、次の step 4 に備えましょう。

step 4 が来て prepublish が本来の姿になったら 、速やかに prepublishOnly のものを prepublish へ戻して「 prepublishOnly を使用していない状態」にし、 step 5 へ備えましょう。


調査内容


作業したリポジトリ(どこで、なにを、どうやってやったか)

https://github.com/ndxbn/npm_prepbulish_migration_test/

prepublish と prepare の(現状のパット見よくわからない状態になった)発端的なものは、 https://docs.npmjs.com/misc/scripts#prepublish-and-prepare とか https://github.com/npm/npm/issues/10074 を見てください。

.travis.yml を見ればわかりますが、複数のバージョンで npm installnpm install ./local_module/sub_projectnpm publishnpm pack を実行してます。

どの npm-script がどのような順番で実行されたか?を眺めて、なんとなく表にしてみました。

実行結果は、 https://travis-ci.org/ndxbn/npm_prepbulish_migration_test で見れます。

エラーになってるやつは、 npm publishprivate: 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

===
==
==
==
==
==
==
==
==

サブモジュールのものは、なにも実行されない
--
--
--
--
--
--
--
--

prepackpostpack は実装されたのが v5.0.0 です。

見どころは


  • 5.0.0 から prepackpostpack が導入された(今回のはあまり関係ない)

  • 5.6.0 から step 4 になるときに、 prepublish の実行タイミングが変更される

  • step 5 で、 prepublishOnly が非推奨になる

あたりです。

「5.6.0 から step 4 になるときに、 prepublish の実行タイミングが変更される」のは、



  1. In a year or so, make a semver-major bump to npm and make prepublish's behavior match prepublishOnly.
    1年後くらいに、メジャーバージョンを上げて(=破壊的変更だと明示して)、 prepublishprepublishOnly と同じ挙動になるに変更した状態にします。
    (再掲)


を根拠に言っています。

これを忠実にやるのであれば、



  • prepublishprepare の後に実行されるように変更される


  • prepublishprepublishOnly はお互いの実行に依存していないはずだし、入れ替えても問題ないはず

の2点から、「step 4 では、 prepublishprepublishOnly の後に実行される」ように変更されるのではないかと予想しています。


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 のリポジトリに含めています