54
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

npm の prepublish と prepare の変遷

Last updated at Posted at 2018-02-06

発端

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 へ備えましょう。

調査内容

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

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

54
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?