パッケージ管理(pnpm workspace)
pnpm・package.json・依存関係の仕組みを理解し、pnpm workspaceを使ったモノリポ構成を使いこなす
STEP 4
パッケージ管理と開発の土台をそろえる
pnpm workspace と依存関係の基本を押さえ、以後の React / Hono 実装の土台を整える段階。
この STEP で作るもの
- • pnpm workspace を前提にした開発リポジトリの理解
この STEP でできるようになること
- • dependencies と devDependencies の違いを説明できる
- • pnpm の基本コマンドで依存関係を扱える
- • モノリポ構成の土台を理解できる
この章で手を動かすこと
- • pnpm の基本コマンドを使える
- • workspace と package.json の役割を説明できる
次に活きる章
pnpmはNode.jsのパッケージマネージャーで、Pythonの pip に相当する。このカリキュラムでは npm の代わりに pnpm を使う。理由は2つ:インストールが高速・ディスク使用量が少ない、そして workspace(モノリポ)機能が標準で使えるため。
コラム: lockfile はなぜ commit するのか
pnpm-lock.yaml は「いま本当に入った依存関係の正確な組み合わせ」を固定するファイルである。package.json だけだと、^ や ~ の範囲で後日別バージョンが入る可能性があり、昨日動いたものが今日だけ壊れることがある。
lockfile を commit しておけば、チーム全員と CI が同じ依存関係を再現できる。ライブラリ更新で不具合が起きたときも、「コード変更」なのか「依存変更」なのかを切り分けやすい。
概念リンク
- pnpm Docs: Lockfile(https://pnpm.io/lockfile)
- npm Docs: package-lock.json(https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json)
書籍
- 『フロントエンドの知識地図』: 依存管理やビルド周辺を広く俯瞰できる
- 『JavaScript Primer』: npm エコシステムの基本理解を補強しやすい
pnpm の基本コマンド
pnpm install # package.jsonの全依存関係をインストール
pnpm add react # reactを追加
pnpm add -D typescript # TypeScriptをdevDependenciesに追加
pnpm remove lodash # パッケージを削除
pnpm dev # scripts.devを実行
pnpm build # scripts.buildを実行
pnpm test # テストを実行
npm の代わりに pnpm と書くだけで、ほぼ同じように使える。
package.json の構造
{
"name": "api",
"version": "0.1.0",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.build.json",
"typecheck": "tsc --noEmit",
"db:push": "drizzle-kit push"
},
"dependencies": {
"hono": "^4.9.0"
},
"devDependencies": {
"typescript": "^5.9.0",
"tsx": "^4.19.2"
}
}
dependencies: アプリの実行に必要なパッケージ
devDependencies: 開発・ビルド時のみ使うパッケージ(TypeScript等)
scripts: pnpm dev のように呼び出せるコマンドの定義
pnpm workspace(モノリポ)
このカリキュラムのリポジトリは pnpm workspace を使ったモノリポ構成になっている:
fullstack_typescript_curriculum/
├── pnpm-workspace.yaml workspaceの設定
├── package.json ルートのパッケージ設定(全体のスクリプト)
├── web/ フロントエンド(React + Vite)
│ └── package.json
└── api/ バックエンド(Hono)
└── package.json
# pnpm-workspace.yaml
packages:
- "web"
- "api"
1つのリポジトリに web/ と api/ という2つの独立したパッケージが共存している。これが**モノリポ(monorepo)**と呼ばれる構成。
ルートからの一括操作
ルートディレクトリから web/ と api/ の両方を操作できる:
# ルートで pnpm dev を実行すると web と api が同時に起動
pnpm dev
# 特定のワークスペースだけ操作する
pnpm --filter api add hono
pnpm --filter web add react-router-dom
# 全ワークスペースに対して実行
pnpm -r typecheck # web と api 両方で型チェック
node_modules とロックファイル
fullstack_typescript_curriculum/
├── node_modules/ ← pnpm が一元管理(gitignore する)
├── pnpm-lock.yaml ← バージョンのロックファイル(gitに含める)
├── web/
│ └── node_modules → ../../node_modules(シンボリックリンク)
└── api/
└── node_modules → ../../node_modules(シンボリックリンク)
pnpm はパッケージを一箇所(~/.pnpm-store)に保存し、シンボリックリンクで参照する。同じパッケージを複数プロジェクトで使っても1回しかダウンロードしない。
pnpm-lock.yaml はインストールした各パッケージの正確なバージョンを記録する。これをGitにコミットすることでチーム全員が同じバージョンを使えることが保証される。
セマンティックバージョニング
バージョン番号 4.9.0 は メジャー.マイナー.パッチ の意味:
- パッチ(4.9.0 → 4.9.1): バグ修正のみ
- マイナー(4.9.0 → 4.10.0): 新機能追加、後方互換あり
- メジャー(4.9.0 → 5.0.0): 破壊的変更、移行作業が必要
^4.9.0(キャレット): メジャーを固定、マイナー・パッチは最新を許可
参考リソース
- pnpm公式(https://pnpm.io/ja/)
- pnpm workspace(https://pnpm.io/ja/workspaces)
- Semantic Versioning(https://semver.org/lang/ja/)
確認クイズ
Q1. pnpm が npm より優れている点を2つ挙げよ。
正解: インストールが高速でディスク使用量が少ない、workspace(モノリポ)機能が標準で使える
解説: pnpm はパッケージをグローバルストア(~/.pnpm-store)に一度だけ保存し、各プロジェクトからシンボリックリンクで参照する。そのためnpmのように同じパッケージを何度もダウンロードせず、ディスクを節約できる。
Q2. pnpm add -D typescript を実行すると、package.json のどのフィールドにtypescriptが追加されるか。
- A. dependencies
- B. devDependencies
- C. scripts
- D. peerDependencies
正解: B
解説: -D フラグは devDependencies への追加を指定する。TypeScriptはコンパイル時のみ使用し、実行環境では不要なため devDependencies に入れるのが正しい。本番サーバーで不要なパッケージを dependencies に入れるとデプロイパッケージが肥大化する。
Q3. pnpm-workspace.yaml はどのような役割を持つファイルか。
正解: モノリポ内のどのディレクトリをワークスペース(個別パッケージ)として認識するかを定義するファイル。
解説: packages: フィールドにディレクトリ名を列挙することで、ルートの pnpm が各パッケージを認識できるようになる。これにより --filter による個別操作や -r による全体一括操作が可能になる。
Q4. モノリポ(monorepo)とはどのような構成を指すか。
正解: 1つのリポジトリに複数の独立したパッケージ(例:web と api)が共存する構成。
解説: モノリポにすることで、フロントエンドとバックエンドを同じリポジトリで管理でき、依存関係の共有やルートからの一括操作が容易になる。pnpm workspace はこの構成を低コストで実現する。
Q5. pnpm --filter api add hono コマンドは何をするか。
正解: api ワークスペースだけに hono パッケージを追加する。
解説: --filter オプションを使うと、ルートディレクトリにいながら特定のワークスペースのみを対象に操作できる。web/ に移動して pnpm add するのと同等だが、ルートから統一的に操作できる利点がある。
Q6. pnpm -r typecheck コマンドが実行すること、および -r フラグの意味を答えよ。
正解: -r は全ワークスペース(recursive)を対象にするフラグ。web と api 両方の scripts.typecheck を実行する。
解説: -r フラグを使うと pnpm-workspace.yaml で定義された全パッケージに対して同じコマンドを一斉実行できる。CIで全パッケージの型チェックやテストを実行するときに便利。
Q7. pnpm において node_modules はどのように管理されるか。
- A. 各パッケージに個別に保存される
- B. グローバルストアに1か所保存しシンボリックリンクで参照する
- C. クラウドに保存される
- D. package.json に埋め込まれる
正解: B
解説: pnpm は ~/.pnpm-store にパッケージを一元保存し、各プロジェクトの node_modules からシンボリックリンクで参照する。同一パッケージを複数プロジェクトで使っても実体は1つのためディスク節約になる。
Q8. pnpm-lock.yaml をGitにコミットすべき理由を答えよ。
正解: チーム全員が同じバージョンのパッケージを使えることを保証するため。
解説: ロックファイルがなければ pnpm install の実行タイミングによってインストールされるパッケージのバージョンが異なる可能性がある。ロックファイルをコミットすることで「自分のPCでは動くが他の人の環境では動かない」問題を防ぐ。
Q9. バージョン ^4.9.0 のキャレット(^)は何を意味するか。
正解: メジャーバージョンを固定し、マイナーとパッチは最新を許可する。つまり 4.x.x の最新が許可される(5.0.0 は対象外)。
解説: セマンティックバージョニングではメジャー変更が破壊的変更を意味するため、キャレットを使うことで安全な範囲で自動更新できる。ただし依存関係の完全固定が必要な場合はロックファイルと組み合わせて使う。
Q10. 4.9.0 → 5.0.0 のようなメジャーバージョンアップが行われる場合、何が起きる可能性があるか。
正解: 破壊的変更(breaking change)が含まれており、既存のコードが動かなくなる可能性がある。移行作業が必要になる。
解説: セマンティックバージョニングの規約では、メジャーバージョンアップは後方互換性のない変更を含むことを示す。APIの引数変更・関数の廃止・動作の変更などが含まれるため、アップグレード前にリリースノートやマイグレーションガイドを確認する必要がある。
この章で手を動かすときの確認
この章は 導入編 の STEP 4にある 「手を動かす」 の章です。本文を読み終えたあと、次の観点だけ確認すると次へ進みやすくなります。
この章で確認すること:
- pnpm の基本コマンドを使える
- workspace と package.json の役割を説明できる
次に活きる章:
- Webアプリアーキテクチャ全体像
- Reactコンポーネント設計の基礎
補助参照: