🔍
Node.jsのモジュール解決とnpm workspacesにおけるモノレポ参照の仕組み
はじめに
モノレポを利用しはじめるにあたって、npm workspacesで他パッケージのモジュールを参照する仕組みを調べたのでまとめました。
1. Node.jsの基本モジュール解決ルール
Node.jsでは、require('foo') のようにモジュール名が渡されたとき、次の順で探索します。
-
コアモジュールの確認
httpやfsなど、Node.jsに組み込みのモジュールがあればそれを即座に返す。 -
相対/絶対パス指定の確認
./fooや/path/to/fooのようにパスで指定された場合は、そのパスを直接解決。 -
名前(識別子)による探索
現在のファイルのディレクトリから順に上位へ辿り、node_modulesを探す。
例:/project/src/index.jsからrequire('foo')を実行した場合
/project/src/node_modules/foo
/project/node_modules/foo
/node_modules/foo
2. npm workspacesの動作
npm workspacesは、モノレポ構成で複数パッケージを一括管理する仕組みです。
- ルートの
package.jsonに"workspaces"フィールドを設定 - ルートで
npm installを実行すると- 共通依存をルートの
node_modulesにhoist(集約) - 各ワークスペースパッケージをルートの
node_modulesにシンボリックリンクとして配置
- 共通依存をルートの
ディレクトリ例:
/project
├── node_modules
│ ├── package-a -> ../packages/package-a (symlink)
│ └── package-b -> ../packages/package-b (symlink)
└── packages
├── package-a
│ └── package.json
└── package-b
└── package.json
3. モノレポで他パッケージを参照できる理由
例として /project/packages/package-b/index.js で
require('package-a');
を実行した場合のNode.jsの探索順はこうなります。
- /project/packages/package-b/node_modules/package-a → 存在しない
- /project/packages/node_modules/package-a → 存在しない
- /project/node_modules/package-a → symlinkがあるのでヒット
- 以降の探索は不要
つまり、Node.js自体は通常通り「下から上へ順に」探索していますが、npm workspacesが各ワークスペースパッケージをルートの node_modules にシンボリックリンクとして配置しているため、他のワークスペースを参照できます。
Discussion