npm公開プロセスの深淵:prepareとprepublishOnlyを制し、リリース事故を根絶せよ
多くのエンジニアが「なんとなく」で使い分けている`npm`のライフサイクルスクリプト。しかし、プロダクション環境で「ビルドの成果物が含まれていない」「CIでテストが走らずに公開された」といった事故を防ぐためには、npmの実行エンジンがパッケージをどのようにパッケージングし、レジストリに送り込んでいるかの「裏側」を理解する必要があります。
本記事では、テックリードの視点から、npmの公開ライフサイクルをハックし、開発体験(DX)と堅牢性を両立させるためのアーキテクチャを伝授します。
—
1. ライフサイクルスクリプトの「真実」:なぜ混乱するのか
npmには似た名前のフックが複数存在し、その挙動は「どのタイミングで実行されるか」によって厳密に定義されています。まずは、この不可解な迷宮を論理的に整理しましょう。
実行順序と責務の分離
公開時(`npm publish`)に実行される主なフックの順序は以下の通りです。
1. `prepare`: ローカルでの`npm install`(引数なし)時、および`npm publish`の直前に実行。「配布物の構築」に最適です。
2. `prepack`: パッケージがtarballにパックされる直前に実行。
3. `prepublishOnly`: `npm publish`時のみ実行。「公開前の最終チェック(テスト実行など)」に最適です。
なぜ`prepare`と`prepublishOnly`を混同してはいけないのか?
`prepare`は`npm install`時にも走ります。ここに重いテストや公開用の制約を含めると、メンバーがライブラリをインストールするたびにCI級の負荷がかかり、開発スピードが鈍化します。逆に、`prepublishOnly`はインストール時には走らないため、ここを「ビルド」に使ってしまうと、レジストリにはソースコードしか上がらず、利用者は動かないコードをダウンロードすることになります。
—
2. 事故ゼロを実現する「ベストプラクティス」設定
公開プロセスを自動化しつつ、ヒューマンエラーを排除するための`package.json`の設計は以下の形が理想です。
{
“name”: “my-library”,
“version”: “1.0.0”,
“files”: [
“dist”
],
“scripts”: {
“build”: “tsc –project tsconfig.json”,
“test”: “jest”,
“prepare”: “npm run build”,
“prepublishOnly”: “npm test && npm run lint”
}
}
この構成が「最強」である理由
- `files`フィールドの活用: `.npmignore`は無視されるリスクがあるため、明示的なホワイトリスト方式(`files`フィールド)を採用します。`dist`ディレクトリ以外はレジストリに上げないという「意思表示」です。
- `prepare`の役割: ライブラリをローカルにインストールした時点で確実に`dist`が生成されるため、開発者は「ビルドし忘れてPRを送る」という恥ずかしいミスから解放されます。
- `prepublishOnly`の番兵: 公開コマンドを叩いた瞬間にテストが走り、失敗すれば即座に公開プロセスが中断されます。これにより、テストが通っていないコードが世界に公開されるリスクを物理的にゼロにします。
—
3. チーム開発で差をつける:生産性を最大化するテクニック
隠れたキーボードショートカット:`npm run`の省略
CLIでの開発効率を極限まで高めるには、`npm run