パッケージマネージャー周り(主にnpm)を整理してみました。
パッケージとは
ソフトウェアのコード、リソースをまとめたもの。
関数、クラス、変数、静的ファイルなどが含まれる。
要は便利な機能をパッケージ化して提供していて、開発者は関数、タグなどを呼んで使うだけで0-1で実装しないで済み、開発効率を上げられる。
例)時刻の操作(day.js)、グラフの描画(chart.js)など
*パッケージをライブラリと呼んでもOK
依存関係とバージョン管理
パッケージは他のパッケージを使用している事がある。これを 依存関係 と呼びます。
例)Expressは裏側で沢山他のパッケージも使用していて、各々が複雑にバージョンが絡んでいる。
そんな複雑なバージョンの依存関係を人間が管理、解決するのは困難。
それをまとめていい感じに管理してくれるのがパッケージマネージャー
パッケージマネージャーとは
プログラムを一貫した方法でインストール、アンインストール、パッケージの依存関係を解決する流れをツールによって管理を自動化するシステム
パッケージマネージャーの種類:
JavaScript (Node.js)
・npm
・yarn=JavaScript (npmより高速、依存関係の解決でnpmより優れていて、安全性が高い)
・pnpm=最新のJSのパケマネ
*npm, yarn複数のパッケージマネージャーを混在させるのはNG!
Python
・pip
Ruby
・Gems
以上言語ごとにあるけど、Mac OS用のbrew
もパッケージマネージャー
npmとは
Node Package Manager
の略
JavaScript (Node.js)のパッケージマネージャーのひとつ。
フロントエンド開発ではNode.js上で動くツールを多用するため、これらのツールのバージョン管理システムの必要性が出て、npmが登場した。
またnpmレジストリ(パッケージのプラットフォーム)というパッケージの公開、検索ができるプラットフォームもある。
ここで公開されているパッケージはnpm
はもちろん、yarn
, pnpm
経由でもインストールができる。誰でもパッケージを公開できるので、危ないもの、更新されてないものもあるので、吟味が必要。
余談
過去のプロジェクトでサービスの核になる機能(複雑で肥大化していた)をアプリ本体から別レポジトリに切り出して、npmレジストリで公開し、外部ライブラリの様に扱う運用にした。
それが更新されると、npmインストールすれば最新が使えるという感じで管理がしやすくなった。
package.jsonとは
Node.jsプロジェクトのパッケージ管理ファイル。
メタ情報(基本情報)や依存関係の定義が記述されていて、とても重要。
インストールすべきパッケージのバージョンが範囲で記載されている。
npm, yarn, pnpmだろうと、このpackage.jsonが使われる(理由はyarn, pnpmはnpmと互換を持って作られてるから)
例)
Reactをインストールすると、package.json
にreactだけ記載される
Reactが依存してるパッケージの詳細は後述するpackage.lock.json
に記載される
"dependencies": {
"react": "18.2.0",
}
devでインストールするケース:
ビルドしたらサーバーには置かなくていいパッケージ(製品そのものに不要、開発段階だけで使うもの)は、インストールコマンドに-dev
をつけてdevDependenciesに入れる。これでリリースビルドには含まれない。
例)ESLint, testツールなど
"devDependencies": {
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
}
また、Node.jsはPCにインストールされて、そこにもpackage.jsonがある(見えない場所)グローバルインストールしたパッケージはそこに記載される。
*グローバルインストール=PC自体に入れるので、どこからでも使える
*通常はプロジェクトのディレクトリ内でローカルインストールをする
package.lock.jsonとは
前述の通り、package.jsonに記載されてるバージョンは以下の様にざっくりとした範囲で書かれている。
※ ^3.4.21
は、3.4.21以上という意味
"dependencies": {
"vue": "^3.4.21"
}
そのため、チーム内で異なるタイミングで、npm install
(パッケージのインストールコマンド、以下npm i
と略語で表記)するとバージョンに差が出て衝突してしまう。
なぜなら、npm iはpackage.json に記載のバージョンの範囲をもとに node_modules のディレクトリにパッケージをインストールするからです。
それを解決するためにpackge.lock.json
が全パッケージの依存関係を解決した完全な情報を作り、バージョンは正確な値を固定で書いてくれる。URLなども記載されている。
→バージョンを固定(Lock)してチーム全員が同じバージョンを使うようにする。
このpackge.lock.jsonの中のバージョンを元にパッケージインストールを実行するにはnpm ci
コマンドを使います。
npm i
とnpm ci
の違い
npm i
- package.json の内容を更新する
- package.json の内容をもとに node_modules のディレクトリにパッケージをインストールする
- node_modules に実際にインストールされたパッケージのバージョン情報が package-lock.json に記述される
npm ci
ciはclean install
のこと
- node_modulesディレクトリを削除して、package-lock.jsonに記載のバージョンに基づいてパッケージをインストールする
- package-lock.json は更新しない
npm ci
を使用するケース
・CI 実行時
・パッケージ追加後のビルドエラーとかで node_modules を空にしてやり直すとき
rm -rf node_modules && npm i
と同じこと
*package.lock.jsonがないプロジェクト初期の場合は、package.jsonを見てnpm i
して、package.lock.jsonを作成する
*yarnの場合はyarn.lockがこれに当たる
なぜわざわざ2つに分かれてるのか?
パッケージマネージャー(npm)がバージョン管理、依存関係の解決がしやすいという理由と、以下の理由で2つに分かれている。
package.json
:人間が見て読めるし、直接編集する事も可能
packge.lock.json
:npmが自動で管理する、量が多く複雑なので読みにくいし、絶対に編集はしない!
パッケージをインストールすると、裏側でそのパッケージが依存するパッケージもインストールする。
それらのバージョンの依存関係の解決をパッケージマネージャー(npm)がやってくれる。
なので、開発中にpackge.lock.jsonに差分が出たらコミットしてOK
node modulesとは
パッケージそのもののコードがここに入るので、大容量。
packge.lock.jsonがあれば再生可能なので、GitHubにあげないのがルール。
また、ここに入ってるコードは参照することはあっても、編集は絶対しない。
パッケージ周りで起こるエラー
よく新規でパッケージをインストールしたり、既存のパッケージのバージョンを上げたりするとビルドできなくなる問題の原因のほとんどがバージョンのコンフリクトによるものかと思います。
パッケージAをバージョンUPすると、依存してるパッケージB、Cもバージョンを上げないといけないなどが多くの原因。