目次
概要
今回のブログポストではYarnのWorkspacesを使用してモノレポを構成する際に、各プロジェクトに必要な依存関係(ライブラリ)を追加する時に発生するHoistingについて紹介します。
ブログシリーズ
このブログはシリーズで作成されました。次のリンクを通じて他のブログポストも確認してください。
- [プロジェクト管理] リポジトリ戦略
- [JavaScript] モノレポ(Monorepo)のためのツール
- [Monorepo] NodeJS のモジュールの読み込み
- [Monorepo] Symlink
- [Monorepo] Yarn Workspaces
- [Monorepo] Yarn Workspaces の依存性の Hoisting
- [Monorepo] Yarn Workspaces のコマンド
- [Monorepo] pnpm を使ってモノレポを作る方法
NodeJS のモジュールの読み込み
NodeJSでimportを使ってモジュールを読み込みする時、NodeJSはまずルートフォルダのnode_modulesを確認し、そのモジュールが見つからない場合、親のnode_modulesを確認し、これを繰り返して最上位フォルダのnode_modulesまで確認します。
今回のブログポストで紹介するHoistingはNodeJSのモジュールの読み込みプロセスを活用した機能です。
依存性の Hoisting
YarnのWorkspacesを使用してモノレポを構成する際に、各プロジェクトに必要な依存性を追加する時、効率的に依存性を管理するため、その依存性はHoistingされて最上位フォルダのnode_modulesにインストールされます。
依存性のHoistingは次のような利点を持ちます。
- 各プロジェクトで共通して使用される依存性は一度だけインストールされるので、メモリを節約することができます。
- 同じバージョンの依存性と異なるバージョンの依存性を分離して管理するため、依存性のバージョンの競合を防ぐことができます。
- 同じ依存性をインストールする場合、一度だけインストールされるため、迅速にインストールすることができ、すでにインストールされた依存性を使用する場合は追加でインストールされないため、キャッシュ効果を得ることができます。
例題
依存性のHoistingを確認するために、例題を作成してみましょう。まず、次のようにフォルダとファイル構造を作成します。
.
├── package.json
└── src
├── module-a
│ ├── index.js
│ └── package.json
└── module-b
├── index.js
└── package.json
module-aのpackage.jsonは次のようになります。
// src/module-a/package.json
{
"name": "module-a",
"version": "1.0.0",
"main": "index.js"
}
module-bのpackage.jsonは次のようになります。
// src/module-b/package.json
{
"name": "module-b",
"version": "1.0.0",
"main": "index.js"
}
そして、module-bのindex.jsは次のようになります。
// src/module-b/index.js
console.log('module-b');
module-aのindex.jsは次のようになります。
// src/module-a/index.js
console.log('module-a');
require('module-b');
最後に、ルートフォルダのpackage.jsonファイルを次のように修正します。
{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["src/*"]
}
}
YarnのWorkspacesを使うために、package.jsonのwordspacesにpackagesが設定されていることが確認できます。YarnのWorkspacesについての詳細は次のブログポストを参照してください。
依存性のインストール
それでは、次のコマンドを実行して依存性をインストールします。
yarn install
すると、次のようにnode_modulesフォルダにmodule-aとmodule-bのSymlinkが作成されていることが確認できます。
.
├── node_modules
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ │ ├── index.js
│ │ └── package.json
│ └── module-b
│ ├── index.js
│ └── package.json
└── yarn.lock
依存性の追加
次はmodule-aに依存性を追加して、その依存性がHoistingされるか確認してみましょう。module-aのpackage.jsonファイルであるsrc/module-a/package.jsonファイルを開いて、次のように修正します。
{
"name": "module-a",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^3"
}
}
そして、次のコマンドを実行して依存性をインストールします。
yarn install
すると、次のようにルートフォルダのnode_modulesにHoistingされたlodashがインストールされていることが確認できます。
.
├── node_modules
│ ├── lodash
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ └── module-b
└── yarn.lock
Hoisting された依存性の使用
Hoistingされた依存性を使うことができるか確認するために、src/module-a/index.jsファイルを開いて、次のように修正します。
const _ = require('lodash');
console.log('module-a');
require('module-b');
console.log(_.flatten([1, [2, 3, 4]]));
そして、次のコマンドを実行してmodule-aを実行します。
node ./src/module-a
すると、次のようにlodashが正しくインポートされることが確認できます。
module-a
module-b
[ 1, 2, 3, 4 ]
これで、Hoistingされた依存性を使用することができることが確認できました。
依存性のバージョンの競合
同じ依存性の異なるバージョンがインストールされた場合、どのように動作するか確認してみましょう。module-bのpackage.jsonファイルであるsrc/module-b/package.jsonファイルを開いて、次のように修正します。
{
"name": "module-b",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^4"
}
}
その後、次のコマンドを実行して依存性をインストールします。
yarn install
すると、次のようにバージョンが異なる依存性がモジュールフォルダのnode_modulesにインストールされていることが確認できます。
.
├── node_modules
│ ├── lodash
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ │ ├── index.js
│ │ └── package.json
│ └── module-b
│ ├── index.js
│ ├── node_modules
│ │ └── lodash
│ └── package.json
└── yarn.lock
nohoist オプション
Hoistingされた依存性を使用せず、各プロジェクトのnode_modulesに依存性をインストールしたい場合、nohoistオプションを使うことができます。ルートフォルダのpackage.jsonファイルを開いて、次のようにnohoistオプションを追加します。
{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["src/*"],
"nohoist": ["**/lodash"]
}
}
そして、yarn.lockファイルを削除した後、次のコマンドを実行して依存性をインストールします。
# rm yarn.lock
yarn install
すると、次のようにlodashがHoistingされず、各プロジェクトのnode_modulesにインストールされていることが確認できます。
.
├── node_modules
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ │ ├── index.js
│ │ ├── node_modules
│ │ │ └── lodash
│ │ └── package.json
│ └── module-b
│ ├── index.js
│ ├── node_modules
│ │ └── lodash
│ └── package.json
└── yarn.lock
完了
これで、YarnのWorkspacesを使用してモノレポを構成する際に、各プロジェクトに必要な依存性を追加する時、依存性が追加される際に発生するHoistingについて紹介しました。また、Hoistingされることを防ぐためのnohoistオプションについても紹介しました。
皆さんもYarnのWorkspacesを使用してモノレポを構成する際に、依存性のHoistingとnohoistを活用して効率的に依存性を管理してみてください。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。






