[Monoepo] Yarn Workspaces

2024-04-13 hit count image

Let's see how to use Yarn's Workspaces to set up a monorepo for a project developed in JavaScript.

Outline

In this blog post, I will introduce how to set up a monorepo using Yarn’s Workspaces.

Blog Series

This blog is a series. Please check other blog posts through the following links.

Yarn Workspaces

In the previous blog post(Tools for Monorepo), I introduced Yarn’s Workspaces.

In this blog post, I will introduce how to set up a monorepo using Yarn’s Workspaces.

Yarn’s Workspaces is a feature that allows you to manage multiple projects in a single codebase using the Symlink introduced in the previous blog post. This feature makes it easy to set up a monorepo.

Example

Let’s create an example to check the Workspace feature provided by yarn. First, create a folder and file structure as follows.

.
└── 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');

Finally, the index.js of module-a is as follows.

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

require('module-b');

After configuring the files like this, run the following command to check if the module is loaded correctly.

node src/module-a/index.js

After running the command, you can see the following error.

module-a
node:internal/modules/cjs/loader:1073
  throw err;
  ^

Error: Cannot find module 'module-b'
Require stack:
- /Users/deku/temp/temp/src/module-a/index.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1070:15)
    at Module._load (node:internal/modules/cjs/loader:923:27)
    at Module.require (node:internal/modules/cjs/loader:1137:19)
    at require (node:internal/modules/helpers:121:18)
    at Object.<anonymous> (/Users/deku/temp/temp/src/module-a/index.js:3:1)
    at Module._compile (node:internal/modules/cjs/loader:1255:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1309:10)
    at Module.load (node:internal/modules/cjs/loader:1113:32)
    at Module._load (node:internal/modules/cjs/loader:960:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/Users/deku/temp/temp/src/module-a/index.js' ]
}

Yarn Workspaces Setup

Now let’s use Yarn’s Workspaces to make module-a use module-b. First, run the following command in the root folder(/) to prepare to use Yarn’s Workspaces.

yarn init -y

After that, the following package.json will be created in the root folder.

{
  "name": "monorepo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}

In order to use Yarn’s Workspaces, you need to modify this package.json file as follows.

{
  "name": "monorepo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "workspaces": {
    "packages": ["src/module-a", "src/module-b"]
  }
}

Monorepo has multiple projects, so it is not necessary to deploy to places like the npm registry. Therefore, set private to true to prevent the monorepo from being deployed. Yarn V1 requires this setting, but Yarn V2 does not require it. It is recommended to set private to true to manage it safely. Of course, individual projects in the monorepo do not need to set private because they can be deployed.

Workspaces can be set as an array([]) or an object({}). Yarn recommends writing it in object format. In Workspaces, an array is created with the key packages and each module is added. You can specify each one like this, but you can also specify all modules simply by using * as follows.

{
  "name": "monorepo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "workspaces": {
    "packages": ["src/*"]
  }
}

Check Yarn Workspaces

We are now ready to use Yarn’s Workspaces. Now run the following command to install the packages.

yarn install

Then you can see that the node_modules folder is created in the root folder and each module is connected via 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

Now let’s check if module-a can use module-b by running the following command.

node src/module-a/index.js

Then you can see that module-a is executed without any problems.

module-a
module-b

Of course, since it is connected via Symlink, if you modify the code of module-b, you can use the modified code in module-a. Open the src/module-b/index.js file and modify it as follows.

console.log('module-b!!!');

Then run the following command to check if module-a works correctly.

node src/module-a/index.js

After running the command, you can see that the modified content is displayed correctly.

module-a
module-b!!!

.gitignore

If you are managing the source code with Git, create a .gitignore file and modify it as follows to exclude the node_modules folder from Git.

# .gitignore
node_modules

Completed

Done! We’ve seen how to use Yarn’s Workspaces to set up a monorepo. Yarn’s Workspaces basically works with Symlink, so it’s good to understand Symlink. If you want to know more about Symlink, please see to the previous blog post.

Now you can try using Yarn’s Workspaces to set up a monorepo.

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

App promotion

You can use the applications that are created by this blog writer Deku.
Deku created the applications with Flutter.

If you have interested, please try to download them for free.

Posts