[Monoepo] Yarn Workspacesの依存性のHoisting

2024-04-12 hit count image

YarnのWorkspacesを使ってMonorepoを構成する際に、各プロジェクトに必要な依存性を追加する時、動作するHoistingについて説明します。

概要

今回のブログポストではYarnWorkspacesを使用してモノレポを構成する際に、各プロジェクトに必要な依存関係(ライブラリ)を追加する時に発生するHoistingについて紹介します。

ブログシリーズ

このブログはシリーズで作成されました。次のリンクを通じて他のブログポストも確認してください。

NodeJS のモジュールの読み込み

NodeJSimportを使ってモジュールを読み込みする時、NodeJSはまずルートフォルダのnode_modulesを確認し、そのモジュールが見つからない場合、親のnode_modulesを確認し、これを繰り返して最上位フォルダのnode_modulesまで確認します。

今回のブログポストで紹介するHoistingNodeJSのモジュールの読み込みプロセスを活用した機能です。

依存性の Hoisting

YarnWorkspacesを使用してモノレポを構成する際に、各プロジェクトに必要な依存性を追加する時、効率的に依存性を管理するため、その依存性はHoistingされて最上位フォルダのnode_modulesにインストールされます。

依存性のHoistingは次のような利点を持ちます。

  • 各プロジェクトで共通して使用される依存性は一度だけインストールされるので、メモリを節約することができます。
  • 同じバージョンの依存性と異なるバージョンの依存性を分離して管理するため、依存性のバージョンの競合を防ぐことができます。
  • 同じ依存性をインストールする場合、一度だけインストールされるため、迅速にインストールすることができ、すでにインストールされた依存性を使用する場合は追加でインストールされないため、キャッシュ効果を得ることができます。

例題

依存性のHoistingを確認するために、例題を作成してみましょう。まず、次のようにフォルダとファイル構造を作成します。

.
├── package.json
└── src
    ├── module-a
    │   ├── index.js
    │   └── package.json
    └── module-b
        ├── index.js
        └── package.json

module-apackage.jsonは次のようになります。

// src/module-a/package.json
{
  "name": "module-a",
  "version": "1.0.0",
  "main": "index.js"
}

module-bpackage.jsonは次のようになります。

// src/module-b/package.json
{
  "name": "module-b",
  "version": "1.0.0",
  "main": "index.js"
}

そして、module-bindex.jsは次のようになります。

// src/module-b/index.js
console.log('module-b');

module-aindex.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/*"]
  }
}

YarnWorksapcesを使うために、package.jsonwordspacespackagesが設定されていることが確認できます。YarnWorkspacesについての詳細は次のブログポストを参照してください。

依存性のインストール

それでは、次のコマンドを実行して依存性をインストールします。

yarn install

すると、次のようにnode_modulesフォルダにmodule-amodule-bSymlinkが作成されていることが確認できます。

.
├── 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-apackage.jsonファイルであるsrc/module-a/package.jsonファイルを開いて、次のように修正します。

{
  "name": "module-a",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "lodash": "^3"
  }
}

そして、次のコマンドを実行して依存性をインストールします。

yarn install

すると、次のようにルートフォルダのnode_modulesHoistingされた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-bpackage.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

すると、次のようにlodashHoistingされず、各プロジェクトの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

完了

これで、YarnWorkspacesを使用してモノレポを構成する際に、各プロジェクトに必要な依存性を追加する時、依存性が追加される際に発生するHoistingについて紹介しました。また、Hoistingされることを防ぐためのnohoistオプションについても紹介しました。

皆さんもYarnWorkspacesを使用してモノレポを構成する際に、依存性のHoistingnohoistを活用して効率的に依存性を管理してみてください。

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

アプリ広報

今見てるブログを作成たDekuが開発したアプリを使ってみてください。
Dekuが開発したアプリはFlutterで開発されています。

興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。

Posts