Contents
Outline
In this blog post, I will introduce how Hoisting that occurs when adding dependencies (libraries) to each project when configuring a Monorepo using Yarn’s Workspaces.
Blog Series
This blog is a series. Please check other blog posts through the following links.
- [Project Management] Repository Strategy
- [JavaScript] Tools for Monorepo
- [Monorepo] Node JS module resolution
- [Monorepo] Symlink
- [Monorepo] Yarn Workspaces
- [Monorepo] Dependency Hoisting of Yarn Workspaces
- [Monorepo] Command of Yarn Workspaces
- [Monorepo] Creating a Monorepo with pnpm
NodeJS Module Resolution
When importing modules using import in NodeJS, it first checks the node_modules of the root folder, and if the module is not found, it checks the parent’s node_modules, and repeats this process until it checks the node_modules of the top folder.
In this blog post, I will introduce the Hoisting that uses the module loading process of NodeJS.
Hoisting of Dependencies
In configuring a Monorepo using Yarn’s Workspaces, when adding dependencies to each project, the dependencies are hoisted and installed in the top folder to efficiently manage dependencies.
The Hoisting of dependencies in Yarn’s Workspaces has the following advantages.
- Dependencies that are commonly used in each project are installed only once to save memory.
- By separating dependencies of the same version and different versions, it prevents dependency version conflicts.
- When installing the same dependency, it is installed only once, so it can be installed quickly, and if the already installed dependency is used, it is not installed additionally, so caching effect can be obtained.
Example
Let’s create an example to check the Hoisting of dependencies. First, create the folder and file structure as follows.
.
├── package.json
└── src
├── module-a
│ ├── index.js
│ └── package.json
└── module-b
├── index.js
└── package.json
The package.json of module-a is as follows.
// src/module-a/package.json
{
"name": "module-a",
"version": "1.0.0",
"main": "index.js"
}
The package.json of module-b is as follows.
// src/module-b/package.json
{
"name": "module-b",
"version": "1.0.0",
"main": "index.js"
}
And the index.js of module-b is as follows.
// src/module-b/index.js
console.log('module-b');
The index.js of module-a is as follows.
// src/module-a/index.js
console.log('module-a');
require('module-b');
Lastly, modify the package.json file in the root folder as follows.
{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["src/*"]
}
}
You can see that the packages is set to workspaces in the package.json to use Yarn’s Workspaces. For more information on Yarn’s Workspaces, please refer to the following blog post
Dependency Installation
Now, let’s install the dependencies.
yarn install
And then, you can see the Symlink of module-a and module-b in the node_modules as follows.
.
├── 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
Adding Dependencies
Now, let’s add the dependency to module-a to check Hoisting. Open the package.json file of module-a in src/module-a/package.json and modify it as follows.
{
"name": "module-a",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^3"
}
}
Then, run the following command to install the dependencies.
yarn install
Then, you can see that the lodash is installed and Hoisted in the node_modules of the root folder.
.
├── node_modules
│ ├── lodash
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ └── module-b
└── yarn.lock
Using Hoisted Dependencies
To see if you can use the Hoisted dependencies, open the src/module-a/index.js file and modify it as follows.
const _ = require('lodash');
console.log('module-a');
require('module-b');
console.log(_.flatten([1, [2, 3, 4]]));
And then, run the following command to run module-a.
node ./src/module-a
Then, you can see that lodash is well imported as follows.
module-a
module-b
[ 1, 2, 3, 4 ]
By this, you see the Hoisted dependencies can be used.
Dependency Version Conflict
Let’s see how it works when different versions of the same dependency are installed. Open the package.json file of module-b in src/module-b/package.json and modify it as follows.
{
"name": "module-b",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^4"
}
}
And then, run the following command to install the dependencies.
yarn install
Then, you can see that conflicting version of the lodash is installed in the node_modules of the module folder.
.
├── 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 option
If you want to install dependencies in each project’s node_modules without using the Hoisted dependencies, you can use the nohoist option. Open the package.json file in the root folder and add the nohoist option as follows.
{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["src/*"],
"nohoist": ["**/lodash"]
}
}
And then, delete the yarn.lock file and run the following command to install the dependencies.
# rm yarn.lock
yarn install
Then, you can see that the different versions of the dependencies are installed in the node_modules of each module folder.
.
├── 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
Complete
Done! We’ve seen how Hoisting works when adding dependencies to each project in configuring a Monorepo using Yarn’s Workspaces. We’ve also seen the nohoist option to prevent dependencies from being Hoisted.
I hope this post will help you understand how Hoisting works when adding dependencies to each project in configuring a Monorepo using Yarn’s Workspaces. Also, I hope you can efficiently manage dependencies using Hoisting and nohoist in Yarn’s Workspaces.
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku.Deku created the applications with Flutter.If you have interested, please try to download them for free.



