npm とは何か
npm という名前には、実は3つの顔があります。まずは レジストリ としての顔で、npmjs.com にある JavaScript パッケージの巨大な公開データベースのことです。次に コマンドラインツール としての顔で、Node.js に同梱されていて、パッケージのインストールや管理を担当します。そしてもうひとつが 仕様 としての顔で、プロジェクトが必要とするものを記述する package.json のフォーマットがこれにあたります。
たとえば npm install express を実行すると、CLI がレジストリに問い合わせて express とその依存関係をまるごとダウンロードし、node_modules というフォルダにファイルを配置したうえで、パッケージ名とバージョンを package.json に書き込みます。基本的にはこの流れの繰り返しです。
Node.js をインストール済みなら、npm もすでに入っているはずです。確認してみましょう。
node --version
npm --version
バージョンが両方とも表示されれば準備OKです。
プロジェクトを始める: npm init 使い方
npm プロジェクトには必ず package.json が必要です。これはプロジェクトの名前・バージョン・スクリプト・依存パッケージを記録しておくマニフェスト(設定ファイル)です。一番手っ取り早い作り方は npm init -y で、すべての項目をデフォルトのまま作成してくれます:
mkdir my-app
cd my-app
npm init -y
そうすると、次のような内容が書き出されます。
{
"name": "my-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
-y を付けずに実行すれば、npm が各フィールドを対話形式で聞いてくれます。どちらにしても最終的には package.json ができあがり、以降のすべてはこのファイルに紐づいていきます。各フィールドの詳細は次のページで解説します。
パッケージをインストールする
package.json ができたら、npm install(省略形は npm i)でパッケージをインストールできます。
npm install lodash
次の3つが同時に行われます。
- npm が
lodashとその依存パッケージをnode_modules/にダウンロードする package.jsonのdependenciesに"lodash": "^4.17.21"(最新版ならそのバージョン)を追記する- 依存ツリー内すべてのパッケージの正確なバージョンを記録した
package-lock.jsonを生成する
これで使えるようになります。
require(ESM プロジェクトなら import)を呼び出すと、Node は node_modules の中を探してパッケージを見つけてくれます。パスを自分で書く必要はなく、Node のモジュールリゾルバがよしなに処理してくれるわけです。
dependencies と devDependencies の違い
すべてのパッケージが本番環境で必要になるわけではありません。テストフレームワークや Linter、バンドラーなどは開発中にしか使いません。こうしたパッケージは --save-dev(または -D)を付けてインストールします:
npm install --save-dev jest
npm install -D eslint prettier
これらは dependencies ではなく devDependencies に入ります:
{
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.57.0",
"prettier": "^3.2.5"
}
}
本番サーバーでは npm install --omit=dev を使うと dev セクションをまるごとスキップしてくれるので、インストールが軽くて速くなります。この切り分けは地味に見えて実はかなり重要で、たとえば webpack を誤って dependencies に入れてしまうと、本番デプロイのたびに無駄な容量を食うことになります。
まとめてインストールする
すでに package.json がある既存リポジトリをクローンした場合は、パッケージを1つずつ指定する必要はありません。次のコマンドを叩くだけです:
npm install
引数なしで実行すると、npm は package.json を読み込み(package-lock.json に記録された正確なバージョンを尊重しつつ)、依存ツリー全体を node_modules にインストールします。リポジトリをクローンしてきて最初に叩くコマンドがこれ、という感じですね。
だからこそ node_modules は .gitignore に入れておくものです。lockfile から再現できますし、サイズは巨大、しかも npm install を走らせるたびに中身が変わります。コミットすべきは package.json と package-lock.json だけ。node_modules は各自で生成してもらえば十分です。
パッケージをアップデートする
npm outdated を使うと、古くなっているパッケージを確認できます。
npm outdated
Current(現在のバージョン)、Wanted(求められるバージョン)、Latest(最新バージョン)という3つの列を持つ表が表示されます。Wanted は package.json で指定した範囲内で利用できる最新バージョンのことです(たとえば ^4.17.21 であれば 5.0.0 未満までが対象になります)。一方の Latest は実際に公開されている最新バージョンで、まだ導入していないメジャーアップデートが含まれている場合もあります。
許可された範囲内でアップデートするには、次のようにします。
npm update
最新バージョン(メジャーアップデートを含む)に一気に上げたい場合は、@latest を付けて再インストールします:
npm install lodash@latest
メジャーバージョンの更新はコードを壊す可能性があります。バージョン番号が上がるのは、まさにそのサインです。メジャーアップデートを適用する前に、必ずチェンジログに目を通しましょう。
パッケージのアンインストール
パッケージの削除も、インストールと同じ感覚で行えます。
npm uninstall lodash
これで node_modules から削除され、package.json のエントリも消えます。開発用の依存だった場合は -D を付けておきましょう(npm 側で判別してくれますが、明示しておくとスクリプトで変な挙動を起こしにくいです)。
グローバルインストールとローカルインストールの違い
基本的に、インストールはローカル(プロジェクト直下の node_modules に固定)で済ませるのがセオリーです。例外は、どこからでも呼び出したいコマンドラインツール類ですね。
npm install -g typescript
npm install -g http-server
グローバルインストールを実行すると、ツールはシステム全体の共通領域に配置され、bin のエントリが PATH に追加されるため、どのディレクトリからでも tsc や http-server を呼び出せるようになります。ただし、グローバルインストールはプロジェクトごとに管理されないので、マシンごとにバージョンがズレてしまうことがあります。
一度きりのコマンド実行なら、npm に同梱されている npx を使うのがちょうどいい落としどころです。
npx create-react-app my-app
npx prettier --write .
npx は、パッケージをグローバルインストールせずにそのまま実行できるコマンドです。必要なときだけ取ってきて動かし、終わったらそれっきり。一度しか使わないようなツールなら、グローバルに入れっぱなしにするよりずっとスッキリします。
最低限おさえておきたいコマンド集
実際に日々使うのは、だいたいこのあたりです。
npm init -y # package.json を作成
npm install # package.json 内のすべてをインストール
npm install <pkg> # ランタイム依存関係を追加
npm install -D <pkg> # 開発用依存関係を追加
npm install -g <pkg> # CLI ツールをグローバルにインストール
npm uninstall <pkg> # 依存関係を削除
npm outdated # 古くなっているパッケージを確認
npm update # 許可された範囲内で更新
npm install <pkg>@latest # 最新バージョンにジャンプ
npm run <script> # package.json のスクリプトを実行
npx <pkg> # インストールせずにパッケージを実行
npm の基本はだいたいこんなところ。残り(公開、ワークスペース、スコープ付きパッケージなど)は、必要になったタイミングで覚えていけば十分です。
node_modules の中身と仕組み
最後にもう1つ、頭に入れておきたいイメージの話を。node_modules は、プロジェクトが依存しているすべてのパッケージと、それらが依存している パッケージを芋づる式にまとめて入れた、ほぼフラットなフォルダです。1つインストールしただけで100個くらい一緒に入ってくることもありますが、それが普通です。npm は可能な限り重複排除してくれるので、同じバージョンの lodash に依存している2つのパッケージは1つのコピーを共有します。
ロックファイル(package-lock.json)には、それらすべてのパッケージについて、実際に解決された正確なバージョンが記録されています。これがビルドの再現性を支えている仕組みで、同じロックファイルから npm install すれば、別々の開発者でも、何ヶ月経っていても、バイト単位で同じツリーが手に入ります。
node_modules は「生成物」として扱ってください。中のファイルを直接書き換えても、次に誰かがインストールすればきれいに消えてしまいます。
次は package.json
package.json は、npm が裏で読み書きし続けているファイルです。scripts、main、type、バージョン範囲の指定、engines といったフィールドを理解できると、npm は「よくわからないブラックボックス」から「自分でコントロールできるツール」に変わります。次回はその話をしていきます。
よくある質問
npmとは何ですか?
npmはNode.js標準のパッケージマネージャーです。Node.jsに同梱されており、JavaScriptパッケージの巨大な公開レジストリをホストしています。インストール・更新・公開を行うCLIツールも提供されています。たとえば npm install lodash を実行すると、npmがレジストリからlodashをダウンロードして node_modules に配置し、package.json にも記録してくれます。
dependenciesとdevDependenciesの違いは?
dependencies は本番環境でアプリを動かすのに必要なパッケージで、express や react などが該当します。一方 devDependencies は開発時やビルド時にしか使わないもの(テストランナー、バンドラー、リンターなど)です。後者は npm install --save-dev <pkg>(短縮形は -D)でインストールします。本番環境では npm install --omit=dev を使えば devDependencies をスキップできます。
node_modulesはGitにコミットすべき?
コミットしないでください。node_modules は簡単に数百MBに膨らみますし、package.json と package-lock.json があれば完全に再現可能です。.gitignore に追加して、代わりにlockfileをコミットしましょう。こうしておけば、リポジトリをクローンした人が npm install を実行するだけで、まったく同じ依存関係ツリーが手に入ります。
npmのグローバルインストールとローカルインストールはどう違う?
ローカルインストール(npm install <pkg>)は、パッケージをプロジェクト直下の node_modules に入れて package.json に記録します。グローバルインストール(npm install -g <pkg>)はシステム全体にインストールするもので、どこでも使いたいCLIツール向けです。プロジェクトの依存関係はローカルで入れるのが基本。そうすればプロジェクトごとにバージョンを固定できます。