12k
All articles

npm Workspacesを使い始める

npm workspacesの設定、コマンド、制限を解説。モノレポを管理し、兄弟パッケージをリンクし、TurborepoやNxを使う基準もわかります。

OpenReplay Team
OpenReplay Team
npm Workspacesを使い始める

npm workspacesは、npmバージョン7から組み込まれた機能で、1つのリポジトリ(モノレポ)内で複数のパッケージをルートから一元管理できます。npm installを1回実行するだけで、共有依存関係が単一のルートnode_modulesにホイストされ、自作パッケージがそこにシンボリックリンクされます。これにより、npm linkなし、再パブリッシュなしでパッケージ間のインポートが解決されます。アプリと共有ライブラリ、あるいはコンポーネントライブラリとそのドキュメントサイトを管理していて、npm linkの煩わしさ、コードのコピー&ペースト、複数リポジトリの管理に疲れているなら、このビルトイン機能がその摩擦を取り除いてくれます。サードパーティツールは一切不要です。本ガイドでは、最小限の設定、正確なコマンドフラグ、実際の制限事項、そしてビルドオーケストレーターを追加すべきタイミングについて解説します。

重要なポイント

  • npm workspacesはnpm 7以降に同梱されています。現在の最新リリースはnpm 11.18.0で、npm -vでバージョンを確認できます。
  • 最小構成は2つのファイルだけです。"private": true"workspaces": ["packages/*"]を含むルートのpackage.json、そして各パッケージのpackage.json。あとはルートでnpm installを1回実行すれば、すべてが連携されます。
  • 兄弟パッケージへの依存を追加するには、"*"レンジで名前を指定します。npmはインストール時にシンボリックリンクを作成するため、ソースへの変更はすべてのコンシューマーに即座に反映されます。リビルドや再パブリッシュは不要です。
  • npm workspacesは依存関係の解決とリンクを行いますが、タスクの依存順実行、ビルド出力のキャッシュ、「影響を受けるパッケージ」のグラフ計算は行いません。
  • TurborepoやNxはnpm workspacesの「代替」ではなく「上位レイヤー」として使用してください。npmが依存関係の解決とリンクを担い、これらのツールがタスクオーケストレーションとキャッシングを追加します。

npm workspacesはどのように機能するか

npm workspacesは、共有依存関係を単一のルートnode_modulesにホイストし、自作パッケージをその隣にシンボリックリンクすることで、1つのリポジトリをモノレポに変換します。ルートでnpm installを実行すると、npmはすべてのワークスペースをスキャンし、サードパーティの依存関係をトップレベルに一度だけインストールし、各ローカルパッケージをnameフィールドの値でnode_modulesにリンクします。2つのパッケージが互いに依存している場合、参照はそのシンボリックリンクを通じて解決されます。npm CLIはnpm installの一部としてリンクを自動化しており、手動でnpm linkを実行する必要がなくなります

同じworkspacesフィールドとシンボリックリンクモデルは、Yarn、pnpm、Bunでも採用されているため、この考え方はパッケージマネージャー間で共通して適用できます。この機能はnpm 7で導入されており、それ以降のバージョンであれば動作します。

npm workspacesの最小構成とは

最小構成は2つのファイルです。パッケージの場所を宣言するルートのpackage.jsonと、各パッケージのpackage.jsonです。以下の構造を作成します。

my-monorepo/
├── package.json          # ルート — private、workspacesを宣言
└── packages/
    ├── utils/
    │   └── package.json   # @myorg/utils
    └── app/
        └── package.json   # @myorg/app

ルートのpackage.jsonには2つのフィールドが必要です。

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["packages/*"]
}

"private": trueはルートを誤ってパブリッシュしてしまうことを防ぎ、packages/*グロブはnpmに対してpackages/配下のすべてのディレクトリをワークスペースとして扱うよう指示します。レジストリの名前衝突を避けるために、各パッケージには@myorg/utilsのようなスコープ付きの名前を付けてください。

{
  "name": "@myorg/utils",
  "version": "1.0.0",
  "main": "dist/index.js"
}

ルートでnpm install1回実行します。ロックファイルはルートに1つだけ存在し、個々のパッケージ内にnode_modulesは作成されません。すべてがルートにホイストされます。

パッケージ間の依存関係を追加する

兄弟パッケージへの依存を追加するには、"*"レンジで名前を指定します。npmはインストール時にシンボリックリンクを作成するため、ソースへの変更はすべてのコンシューマーに即座に反映されます。@myorg/appに以下を追加します。

{
  "name": "@myorg/app",
  "dependencies": {
    "@myorg/utils": "*"
  }
}

ルートで再度npm installを実行します。npmはnode_modules/@myorg/utilsからpackages/utilsへのシンボリックリンクを作成し、通常のパブリッシュ済みモジュールと同様にインポートできます。

import { formatDate } from "@myorg/utils";

シンボリックリンクであるため、packages/utilsのソースを変更すると、リビルドや再パブリッシュなしにappへ即座に反映されます。これがnpm linkに対する優位点です。ツール間の注意点として、npmはpnpmやYarn Berryが使用するworkspace:バージョンプロトコルをサポートしていませんworkspace:指定子を渡すとnpmはEUNSUPPORTEDPROTOCOLエラーで失敗します。そのため、npmでは内部パッケージをworkspace:*ではなく、名前とレンジ("*")で参照してください。

日常的なコマンド

単数形と複数形で意味が異なるため、フラグの使い方で混乱しがちです。1つのパッケージに依存関係を追加するには-wを、すべてのパッケージに追加するには--workspacesを使います。1つのワークスペースでスクリプトを実行するには-wを、すべてのワークスペースで実行するには--workspaces --if-presentを使います。後者はそのスクリプトを定義していないパッケージをスキップします。

# 1つのワークスペースに依存関係をインストール
npm install lodash -w @myorg/app

# 1つのワークスペースに開発依存関係をインストール
npm install -D vitest -w @myorg/utils

# すべてのワークスペースに依存関係をインストール
npm install eslint --workspaces

# 1つのワークスペースでスクリプトを実行
npm run build -w @myorg/utils

# すべてのワークスペースでスクリプトを実行(未定義のものはスキップ)
npm run test --workspaces --if-present

-w--workspaceの省略形で、--workspaces(または-ws)はすべてのワークスペースを対象にします。ルートのスクリプトを一度設定しておけば、npm run buildで全体に展開できます。

{
  "scripts": {
    "build": "npm run build --workspaces --if-present",
    "test": "npm run test --workspaces --if-present"
  }
}

グラフが正しくリンクされているか確認するには、npm ls -wsを実行するか、npm query .workspaceでクエリします。

制限事項:npm workspacesができないこと

npm workspacesは依存関係の解決とリンクを行いますが、タスクの依存順実行、ビルド出力のキャッシュ、「影響を受けるパッケージ」のグラフ計算は行いません。アプリがライブラリをインポートしている場合、先にライブラリをビルドする必要があります。npmはトポロジカル順でスクリプトを実行しないため、ワークスペース間に依存関係がある場合にスクリプトを一括実行するとエラーになります。この改善要望は現在もオープンな状態です。明示的に順序を指定するか、npm-run-allを使用してください。

{
  "scripts": {
    "build:utils": "npm run build -w @myorg/utils",
    "build:app": "npm run build -w @myorg/app",
    "build": "npm run build:utils && npm run build:app"
  }
}

さらに2つの注意点があります。

  • ネストされたnode_modules 2つのパッケージが同じ依存関係の互換性のないバージョンを必要とする場合、npmはホイストを停止し、一方のパッケージ内にネストされたコピーをインストールします。ツリーをフラットに保つには、ルートのoverridesフィールドで単一の共有バージョンを固定してください。

    { "overrides": { "lodash": "^4.17.21" } }
  • インストールスクリプトのデフォルト設定が厳格化されます。 2026年7月リリース予定のnpm v12ではallowScriptsのデフォルトがオフに変更されるため、明示的に許可しない限り、npm installは依存関係のpreinstallinstallpostinstallスクリプトを実行しなくなります。ワークスペースがpostinstallprepareのビルドステップに依存している場合は、承認の対応を計画してください。これらの変更はnpm 11.16.0以降で警告として表示されるため、早めに準備できます。

「ReactやVue、Viteとのネイティブ統合がない」という点は欠陥ではなく、設計上のスコープ定義です。workspacesはフレームワーク非依存であることを意図しており、アプリのスキャフォールディングはその役割ではありません。

TurborepoやNxを使うべきタイミング

TurborepoNxはnpm workspacesの「代替」ではなく「上位レイヤー」として使用してください。npmが依存関係の解決とリンクを担い、これらのツールがタスクオーケストレーション、キャッシング、大規模リポジトリ向けの影響グラフビルドを追加します。両者は補完的なレイヤーです。

関心事npm workspacesTurborepo / Nx
パッケージのインストールとリンクnpmに委譲
タスクの依存順実行❌ 手動スクリプト✅ トポロジカル順
ビルド/テストのキャッシング✅ ローカル + リモート
「影響を受けるパッケージ」のビルド✅ 変更ベースのグラフ

順序付きスクリプトが煩雑になってきたとき、CIがすべての変更に対してフルリビルドを実行しているとき、またはコミットが触れたパッケージのみでタスクを実行したいときに導入を検討してください。なお、モダンなLernaはNxをバックエンドとして採用しています。かつての「npm + Lerna」という構成のアドバイスは、このレイヤリングの考え方に統合されています。

npm workspacesは、追加ツールなしで小規模モノレポのニーズの約80%をカバーします。2ファイルの設定をスキャフォールドし、フラグを設定し、ビルド順序を整理してください。オーケストレーターを追加するのは、依存関係の解決ではなくパイプラインがボトルネックになったときだけで十分です。Active LTS版のNode(Node 20は2026年4月30日にサポート終了)で実行し、開始前にnpm -vが7以降を報告することを確認してください。

よくある質問

npm workspacesでは各パッケージにロックファイルが必要ですか?それともルートに1つだけですか?

npm workspacesはリポジトリのルートに単一のpackage-lock.jsonを生成します。各パッケージごとには生成されません。ルートでnpm installを実行すると、すべてのワークスペースの依存関係がまとめて解決され、1つのロックファイルに記録されます。依存関係はルートにホイストされるため、個々のパッケージにはnode_modulesディレクトリが作成されません。この単一ロックファイルモデルにより、すべてのパッケージ間でバージョンの一貫性が保たれます。そのため、インストールは常にルートから実行してください。

パッケージ間に依存関係がある場合、'npm run build --workspaces'が失敗するのはなぜですか?

npmはワークスペーススクリプトをトポロジカル順(依存順)ではなく、ワークスペースが列挙された順に実行するためです。そのため、インポート先のライブラリが存在する前にコンシューマーがビルドされ、'cannot find module'や解決エラーが発生することがあります。これはnpmのオープンな改善要望(issue 4139)として残っています。ライブラリを先にビルドする明示的な順序付きスクリプトを定義するか、npm-run-all、Turborepo、Nxなどのツールを使用して解決してください。

pnpmやYarnのように'workspace:*'プロトコルをnpmで使用できますか?

いいえ、できません。npmはpnpmやYarn Berryが使用するworkspace:バージョンプロトコルをサポートしておらず、workspace:指定子を渡すとEUNSUPPORTEDPROTOCOLエラーで失敗します(npm/cli issue 8845に記載)。npmでは、内部パッケージを名前と通常のレンジ(例:'@myorg/utils': '*')で参照してください。npmはインストール時にシンボリックリンクを作成します。pnpmやYarnのリポジトリをnpmに移行する場合は、すべてのworkspace:指定子を通常のレンジに書き換えてください。

workspacesを使用している場合でも'npm link'は必要ですか?

いいえ、不要です。npm workspacesはnpm installの一部としてリンクを自動化します。各ローカルパッケージをnameフィールドの値でルートのnode_modulesにシンボリックリンクするため、手動でnpm linkを実行する必要がなくなります。パッケージが'*'レンジで兄弟パッケージを依存関係として宣言していれば、ルートでnpm installを1回実行するだけでシンボリックリンクが設定され、ソースパッケージへの変更はすべてのコンシューマーに即座に反映されます。リビルドや再パブリッシュは不要です。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.