目次
概要
今回のブログポストではYarn
のWorkspaces
を使用してモノレポを構成する際に、各プロジェクトに必要な依存関係(ライブラリ)を追加する時に発生するHoisting
について紹介します。
ブログシリーズ
このブログはシリーズで作成されました。次のリンクを通じて他のブログポストも確認してください。
- [プロジェクト管理] リポジトリ戦略
- [JavaScript] モノレポ(Monorepo)のためのツール
- [Monorepo] NodeJS のモジュールの読み込み
- [Monorepo] Symlink
- [Monoepo] Yarn Workspaces
- [Monoepo] Yarn Workspaces の依存性の Hoisting
- [Monoepo] Yarn Workspaces のコマンド
- [Monoepo] 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
のWorksapces
を使うために、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で開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。