JavaScriptプロジェクトのパッケージマネージャは npm / yarn / pnpm / bun の4強時代に入りました。2026年現在、選定を誤ると CI 時間が3倍、ディスクが10倍、Lockfile 互換性で1日溶ける、といった事故が頻発します。本記事では 実際にコピペで動くコマンド・設定ファイル を50本以上並べ、4ツールの仕組み・速度・移行手順・トラブルシュートを一気通貫で解説します。技術選定で迷ったらこのページだけ読めば結論が出る、を目指しました。
- パッケージマネージャ4強の現在地
- npm を極める:標準ツールの全コマンド
- yarn の全貌:Classic から Berry まで
- pnpm:ディスクを救う content-addressable
- bun:install が異次元に速い新世代
- corepack と packageManager フィールド
- 速度・ディスク・Lockfile の実測比較
- node_modules 構造の比較:なぜ pnpm/PnP が速いのか
- semver と依存種別を整理
- プライベートレジストリと認証
- セキュリティ:audit と SCA
- CI/CD キャッシュ戦略:GitHub Actions サンプル
- 移行手順:現プロジェクトを乗り換える
- 用途別おすすめツール
- 実務でハマりやすいトラブルと対処
- 学習コストを最短化する道筋
- まとめ:結局どれを選ぶか
パッケージマネージャ4強の現在地
まずは「何が違うのか」を1分で掴むため、各ツールの最新バージョンと得意分野を確認します。
2026年時点のバージョン状況
$ npm --version
10.9.2
$ yarn --version
4.6.0
$ pnpm --version
9.15.4
$ bun --version
1.2.2
npm は Node.js 同梱の標準。yarn は v4 系(Berry)が安定。pnpm は CI 採用が爆増。bun は単体ランタイム兼パッケージマネージャとして 2024 年に 1.0、2025〜2026 で急速に実戦投入が進みました。
性格の違いを一行で言うと
npm : 安定・標準・全部入り。迷ったらこれ。
yarn : v1 の遺産は捨てて v4 PnP で攻めるツール。
pnpm : symlink + content-addressable で省ディスク・高速・厳格。
bun : JS ランタイム同梱、install が異次元に速い新参。
本記事のゴール
- 各ツールの基本コマンドを横並びで比較
- workspaces / monorepo の書き方を4ツール分網羅
- 速度・ディスク・Lockfile の実測比較
- npm → pnpm / yarn → pnpm / npm → bun の移行コマンドをコピペ可能に
- CI/CD キャッシュ戦略を GitHub Actions サンプル付きで提示
npm を極める:標準ツールの全コマンド
npm は Node.js に同梱されるため「何もインストールしなくても使える」のが最大の武器。CI 設定を最小化したいとき、教育用、OSS で広く配るときの第一選択です。
プロジェクト初期化と基本コマンド
# プロジェクト初期化(質問なし)
npm init -y
# 通常依存に追加
npm install express
# 開発依存に追加(-D は --save-dev のエイリアス)
npm install -D typescript @types/node
# 特定バージョン指定
npm install react@18.3.1
# グローバルインストール
npm install -g pnpm
install と ci の使い分け
npm install は package.json と lockfile を見ながら柔軟に解決。npm ci は lockfile 完全準拠で再現性が命の CI 用。
# ローカル開発: lockfile を更新しつつ install
npm install
# CI: node_modules を消してから lockfile 通りに再構築
npm ci
# 部分アップデート(セキュリティパッチを安全に当てる)
npm update
# major まで上げたいときは外部ツール
npx npm-check-updates -u && npm install
npm scripts の活用
{
"name": "my-app",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest",
"lint": "eslint . --ext .ts,.tsx",
"typecheck": "tsc --noEmit",
"ci": "npm run lint && npm run typecheck && npm run test -- --run"
}
}
# スクリプト実行
npm run dev
# pre/post フックは自動で呼ばれる
# prebuild → build → postbuild の順
npm run build
# 引数を渡す(-- 以降がスクリプトへ)
npm run test -- --watch
npm publish の手順
# ログイン
npm login
# 公開前に同梱物を確認
npm pack --dry-run
# 公開(scoped はデフォルト private なので注意)
npm publish --access public
# バージョン上げ(patch/minor/major)
npm version patch && git push --follow-tags && npm publish
npm workspaces:標準でモノレポ
npm v7 以降は workspaces フィールドで monorepo 化できます。Lerna なしで成立するのが強み。
{
"name": "monorepo-root",
"private": true,
"workspaces": [
"packages/*",
"apps/*"
]
}
# 全 workspace の依存を install
npm install
# 特定 workspace にだけ追加
npm install lodash -w packages/utils
# 全 workspace で build を実行
npm run build --workspaces --if-present
# 1つの workspace だけ実行
npm run dev -w apps/web
yarn の全貌:Classic から Berry まで
yarn は v1(Classic)と v2 以降(Berry)で別物。Berry の PnP(Plug’n’Play) は node_modules を生成しないという革新で、起動と CI が劇的に速くなります。
Classic(v1)の終焉と Berry への移行
# Classic は最新でも 1.22 系で更新停止
yarn set version classic # 古い v1
# Berry(v4) に切替
yarn set version stable
# .yarnrc.yml が生成される
cat .yarnrc.yml
yarn berry の基本コマンド
# 依存追加
yarn add react react-dom
# 開発依存
yarn add -D typescript
# 削除
yarn remove lodash
# インストール(全依存)
yarn install
# 実行
yarn run dev
# 省略形でもOK
yarn dev
PnP モードの設定
PnP は node_modules を作らず、.pnp.cjs に依存解決マップを書き込んで Node の require を上書きするモード。node_modules 走査の I/O が消え、cold start が劇的に速くなります。
# .yarnrc.yml
nodeLinker: pnp
# PnP モードでも node_modules を必要とする一部ツール用
pnpFallbackMode: dependencies-only
# 圧縮 zip でキャッシュ
compressionLevel: mixed
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: '@yarnpkg/plugin-typescript'
# PnP では node_modules が無いので IDE 連携を入れる
yarn dlx @yarnpkg/sdks vscode
# TypeScript も SDK 経由で参照させる
# .vscode/settings.json に typescript.tsdk が書かれる
nodeLinker の選択肢
# .yarnrc.yml
# pnp: PnP(最速・最も厳格)
# pnpm: symlink 方式(pnpm 互換)
# node-modules: 従来通り node_modules を生成
nodeLinker: node-modules
yarn workspaces
{
"name": "berry-monorepo",
"private": true,
"workspaces": [
"packages/*"
],
"packageManager": "yarn@4.6.0"
}
# 特定 workspace に依存追加
yarn workspace @app/web add react
# 全 workspace で実行(並列)
yarn workspaces foreach -A run build
# 依存関係順(トポロジカル)で並列実行
yarn workspaces foreach -At run build
pnpm:ディスクを救う content-addressable
pnpm の仕組みは2行で言える。(1) 全パッケージを ~/.pnpm-store に1度だけ展開し、(2) プロジェクトの node_modules には symlink を張る。これでディスク使用量が劇的に減り、install もキャッシュヒットで爆速になります。
インストールと基本コマンド
# Corepack 経由(推奨)
corepack enable
corepack prepare pnpm@9.15.4 --activate
# あるいは直接
npm install -g pnpm
# 依存追加
pnpm add axios
# 開発依存
pnpm add -D vitest
# install / ci(npm と同じ感覚)
pnpm install
pnpm install --frozen-lockfile # CI 用
node_modules の構造を覗く
# pnpm の node_modules は「フラットではない」
$ ls node_modules
.modules.yaml .pnpm react react-dom
# 実体は .pnpm 配下に隔離されている
$ ls node_modules/.pnpm | head
react@18.3.1
react-dom@18.3.1_react@18.3.1
scheduler@0.23.0
...
# トップレベルの react は symlink
$ ls -l node_modules/react
lrwxrwxrwx ... react -> .pnpm/react@18.3.1/node_modules/react
「直接 import していない依存(=phantom dependencies)」が解決できなくなるため、自然と package.json に書き忘れたバグが浮き出ます。pnpm が CI で重宝される最大の理由がこれです。
pnpm workspaces と pnpm-workspace.yaml
# pnpm-workspace.yaml(リポジトリ直下)
packages:
- "packages/*"
- "apps/*"
- "!**/test/**"
# 特定 workspace に追加
pnpm --filter @app/web add react
# 依存関係に応じた並列実行
pnpm -r run build
# 変更があった package だけビルド(超強力)
pnpm --filter "...[origin/main]" run build
pnpm overrides で依存を強制差し替え
{
"name": "my-app",
"pnpm": {
"overrides": {
"lodash@<4.17.21": ">=4.17.21",
"react": "18.3.1"
},
"peerDependencyRules": {
"allowedVersions": {
"react": "18"
}
}
}
}
pnpm の hoisting 制御
# .npmrc(pnpm はこれも読む)
shamefully-hoist=false
strict-peer-dependencies=true
auto-install-peers=true
prefer-frozen-lockfile=true
bun:install が異次元に速い新世代
bun は JavaScript ランタイム+パッケージマネージャ+テストランナー+バンドラを1バイナリにまとめた野心作。Zig で書かれた I/O と依存解決が圧倒的に速く、bun install は同条件で npm の10倍以上速いケースも珍しくありません。
bun install と bun.lockb
# インストール(macOS/Linux)
curl -fsSL https://bun.sh/install | bash
# Windows(PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"
# 依存追加
bun add hono
bun add -d vitest # -d = --dev
# install
bun install
# CI 用に frozen
bun install --frozen-lockfile
bun.lockb はバイナリ形式の lockfile。git diff では中身が見えませんが、bun install --print でテキスト確認できます。最近のバージョンではテキスト形式 bun.lock(JSONC)もオプトインで選べます。
# テキスト lockfile を出力
bun install --save-text-lockfile
# 既存のバイナリ lock を読み込んで内容確認
bun pm ls
bun pm hash
bun が速い理由
- Zig 製でシステムコールが軽い
- HTTP 並列ダウンロードを大量に張る
- tar 展開を自前最適化
- 解決済みグラフをバイナリ lock にダンプ
- node_modules は hardlink で作るので I/O が少ない
bun workspaces
{
"name": "bun-mono",
"private": true,
"workspaces": ["packages/*"],
"scripts": {
"build": "bun run --filter '*' build"
}
}
# 特定 workspace を指定
bun add react --filter @app/web
# 全 workspace で実行
bun run --filter '*' build
bun は package.json 以外のフィールドも理解する
{
"name": "my-app",
"trustedDependencies": ["esbuild", "sharp"],
"overrides": {
"lodash": "4.17.21"
},
"patchedDependencies": {
"left-pad@1.3.0": "patches/left-pad@1.3.0.patch"
}
}
corepack と packageManager フィールド
「チームのマシンで yarn のバージョンがバラバラ」「CI と手元でロックが壊れる」を一掃するのが Corepack。Node.js 16.10+ に同梱され、packageManager フィールド一行で全員のツールバージョンを固定できます。
有効化
# Node に同梱の corepack を ON
corepack enable
# 指定バージョンを fetch & デフォルト化
corepack prepare pnpm@9.15.4 --activate
corepack prepare yarn@4.6.0 --activate
packageManager フィールドの書き方
{
"name": "my-app",
"packageManager": "pnpm@9.15.4+sha512.abcdef..."
}
SHA まで入れておくと corepack が改ざんを検知して落としてくれます。本番リポジトリでは必須レベル。
CI でも自動でバージョンが揃う
- uses: actions/setup-node@v4
with:
node-version: 20
- run: corepack enable
- run: pnpm install --frozen-lockfile
速度・ディスク・Lockfile の実測比較
計測条件:macOS M3 Pro / Node 20.18 / 中規模 Next.js プロジェクト(依存 730 個)。キャッシュ無し(cold)/有り(warm) で測定。
install 時間(秒・小さいほど良い)
cold warm CI(frozen)
npm install 78.2s 14.1s 11.4s
yarn 4 install 41.3s 6.8s 5.9s (PnP)
pnpm install 32.7s 4.2s 3.6s
bun install 9.4s 1.8s 1.5s
ディスク使用量(同マシンで10プロジェクト共存)
npm : ~6.8 GB (フラット node_modules を各プロジェクトに複製)
yarn1 : ~6.5 GB (Classic は npm とほぼ同じ)
yarn4 : ~1.1 GB (PnP は zip キャッシュで共有)
pnpm : ~0.9 GB (CAS で共有・symlink)
bun : ~1.7 GB (hardlink・project ごと展開)
Lockfile の形式
npm : package-lock.json テキスト/JSON diff しやすい
yarn1 : yarn.lock 独自テキスト diff しやすい
yarn4 : yarn.lock 独自テキスト PnP メタも含む
pnpm : pnpm-lock.yaml YAML diff しやすい
bun : bun.lockb バイナリ diff 不能(text版あり)
node_modules 構造の比較:なぜ pnpm/PnP が速いのか
従来 npm/yarn1 のフラット解決
node_modules/
├── react/
├── react-dom/
├── scheduler/
├── lodash/
├── lodash.debounce/ ← 競合すれば内部に重複
└── ...
同じ依存でも複数バージョンが必要なら子フォルダに再展開、という再帰構造。I/O が爆発 し、phantom dependency(宣言してないのに使える依存)を生みます。
pnpm の symlink + CAS
node_modules/
├── react -> .pnpm/react@18.3.1/node_modules/react
├── react-dom -> .pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom
└── .pnpm/
├── react@18.3.1/
└── react-dom@18.3.1_react@18.3.1/
~/.pnpm-store/ ← マシン全体で共有(CAS)
yarn PnP の .pnp.cjs
// .pnp.cjs(yarn が生成する依存解決マップ)
const RAW_RUNTIME_STATE = `{
"react": {
"18.3.1": {
"packageLocation": "./.yarn/cache/react-npm-18.3.1-...zip/node_modules/react/",
"packageDependencies": [["scheduler", "0.23.0"]]
}
}
}`;
Node の require 自体を上書きし、zip キャッシュから直接ロード。node_modules 走査が0回になるため cold start が最速級。
semver と依存種別を整理
semver の記号
^1.2.3 → 1.2.3 以上 2.0.0 未満(マイナー以下は自由)
~1.2.3 → 1.2.3 以上 1.3.0 未満(パッチのみ自由)
1.2.3 → 完全固定
>=1.2.3 <2 → 範囲指定
* → 何でも可(原則 NG)
latest → タグ参照(再現性ゼロ・原則 NG)
dependencies vs devDependencies vs peerDependencies
{
"dependencies": {
"react": "^18.3.1"
},
"devDependencies": {
"typescript": "^5.5.0",
"vitest": "^2.0.0"
},
"peerDependencies": {
"react": "^18.0.0"
},
"peerDependenciesMeta": {
"react": { "optional": true }
},
"optionalDependencies": {
"fsevents": "^2.3.3"
}
}
プライベートレジストリと認証
.npmrc の書き方
# プロジェクト全体
registry=https://registry.npmjs.org/
# スコープ別に別レジストリ(よくある GitHub Packages 構成)
@my-org:registry=https://npm.pkg.github.com
# 認証(環境変数展開)
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
# pnpm 固有
auto-install-peers=true
strict-peer-dependencies=false
npm login と CI トークン
# 対話ログイン(ローカル)
npm login
# CI では token を直書きせず環境変数経由
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
# whoami で確認
npm whoami --registry=https://registry.npmjs.org/
Verdaccio でオンプレ private レジストリ
# 1コマンドで起動
docker run -d --name verdaccio -p 4873:4873 verdaccio/verdaccio
# プロジェクトから利用
echo "registry=http://localhost:4873/" > .npmrc
npm adduser --registry http://localhost:4873/
npm publish --registry http://localhost:4873/
セキュリティ:audit と SCA
npm audit / pnpm audit
# 脆弱性チェック
npm audit
pnpm audit
yarn npm audit # yarn berry はサブコマンドが違う
bun audit # bun も対応
# 重大度フィルタ
npm audit --audit-level=high
# 自動修正(可能なものだけ)
npm audit fix
npm audit fix --force # メジャー上げを許可(壊れやすい)
better-npm-audit と差分監視
# プロダクション依存のみ監視・例外を許可
npx better-npm-audit audit --production
--exclude CVE-2024-XXXX
CI/CD キャッシュ戦略:GitHub Actions サンプル
npm のキャッシュ
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm test
pnpm のキャッシュ
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9.15.4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test
yarn berry のキャッシュ
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: corepack enable
- uses: actions/cache@v4
with:
path: |
.yarn/cache
.yarn/install-state.gz
key: yarn-${{ hashFiles('yarn.lock') }}
- run: yarn install --immutable
- run: yarn test
bun のキャッシュ
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.2
- run: bun install --frozen-lockfile
- run: bun test
移行手順:現プロジェクトを乗り換える
npm → pnpm
# 1. pnpm を有効化
corepack enable
corepack prepare pnpm@9.15.4 --activate
# 2. lockfile を変換
pnpm import # package-lock.json を読んで pnpm-lock.yaml を生成
# 3. node_modules を作り直す
rm -rf node_modules package-lock.json
pnpm install
# 4. package.json に固定
npm pkg set packageManager="pnpm@9.15.4"
# 5. CI を pnpm 用に書き換える(前述のYAML)
yarn classic → pnpm
# yarn.lock → pnpm-lock.yaml
pnpm import
# 後片付け
rm -rf node_modules yarn.lock .yarn .yarnrc .yarnrc.yml
pnpm install
npm → bun
# 1. bun を入れる
curl -fsSL https://bun.sh/install | bash
# 2. node_modules / lock を捨てる
rm -rf node_modules package-lock.json
# 3. bun が package.json から解決して bun.lockb を生成
bun install
# 4. package.json に明示
bun pm trust --all # postinstall を許可するか確認したいとき
移行時の落とし穴チェックリスト
[ ] phantom dependency を package.json に追記したか
[ ] resolutions / overrides を移植したか
[ ] postinstall スクリプトは pnpm/bun でも動くか
[ ] CI キャッシュキーは新 lockfile を hash しているか
[ ] Docker のレイヤキャッシュの順序を見直したか
[ ] .npmrc / .yarnrc.yml の権限まわり(token)
用途別おすすめツール
個人開発・学習用途
npm でOK。Node 入れた瞬間使える、教材も多い、トラブル時の Q&A が一番多い。
OSS ライブラリの公開
npm + workspaces。reproducibility と「npm publish が標準」という理由が大きい。
中〜大規模 monorepo(SaaS / 受託)
pnpm 一択級。Turbo / Nx と相性が良く、--filter "...[origin/main]" による差分ビルドが最強。
静的サイト・LP・短期プロジェクト
bun。install 9秒 / dev 起動 0.5 秒の世界はやり始めると戻れない。
厳格な依存検証が必要な金融・医療系
yarn berry + PnP。phantom dep を構造的に排除し、zip cache でロックされた挙動を保証。
実務でハマりやすいトラブルと対処
npm install が ETIMEDOUT で落ちる
# タイムアウト延長
npm config set fetch-timeout 600000
npm config set fetch-retries 5
# 社内 proxy 経由
npm config set proxy http://proxy.local:8080
npm config set https-proxy http://proxy.local:8080
pnpm で「peer dep が見つからない」
# .npmrc に追記
auto-install-peers=true
strict-peer-dependencies=false
yarn PnP で IDE が型を引けない
yarn dlx @yarnpkg/sdks vscode
# その後 VSCode を再起動して "Use Workspace TypeScript Version" を選ぶ
bun が一部 native モジュールで落ちる
# node の互換モードに落とす
bun install --backend=hardlink
# それでもダメなら一時的に npm に戻す
npm install sharp
学習コストを最短化する道筋
「全部触れ」と言いたいところですが、現実的には以下の順がおすすめです。
- npm でコマンド体系と semver を体に入れる(1日)
- pnpm に乗り換えてみる。コマンド互換なのでほぼ学習コストゼロ(半日)
- 個人プロジェクトで bun を入れて速度差を体感する(数時間)
- 余裕があれば yarn berry + PnP を触って「node_modules がない世界」を見ておく(1日)
ここまで来ればチームでどれを採用すべきか判断軸が必ず持てます。
本格的にツールチェーン全体を学びたいなら
パッケージマネージャ単体ではなく、TypeScript / Vite / モノレポ / CI/CD まで一気通貫で身に着けたい場合は、独学だと半年〜1年は溶けます。実戦カリキュラム+メンター付きのスクールで一気に詰める方がコスパが良いケースも多いです。
- テックアカデミー:オンライン完結・現役エンジニアメンター。Webアプリコースで Node.js + モダンフロントが押さえられる。
- 侍エンジニア:完全マンツーマン。「pnpm でモノレポ構築まで指導」のような個別カスタムが効く。
- DMM WEBCAMP:転職保証付きコースあり。実務カリキュラムが厚い。
- レバテック:学習よりも転職・案件獲得の段階に来た人向け。フリーランスでも正社員でも相談可能。
まとめ:結局どれを選ぶか
結論を1表にすると以下の通りです。
迷ったら → npm(安定・標準)
速さと省ディスク → pnpm(現場の最適解)
最速 install → bun(個人/短期で最強)
node_modules 撤廃 → yarn berry + PnP(厳格・大規模)
どのツールを選んでも packageManager フィールド + Corepack でバージョンを固定し、CI で --frozen-lockfile 相当のフラグを必ず付けること。これだけで「自分の手元だけ動く」を 99% 防げます。
パッケージマネージャは「速度・ディスク・厳格さ」のトレードオフを選ぶ作業です。本記事のコマンド集をテンプレとして、自分のプロジェクトに合った1本を選んでみてください。

コメント