When working with a monorepo in a TypeScript project, you need to manage the dependencies between different packages correctly. One way to do this efficiently is by using tools like Yarn workspaces, Lerna, or the native TypeScript ’Project References’.
Let’s discuss each approach and how it can be used to manage dependencies in a monorepo.
1. Yarn Workspaces:
Yarn workspaces are a feature introduced in Yarn v1 to manage dependencies for monorepos. It allows you to have multiple packages within one repository and manage their dependencies efficiently, sharing common dependencies and avoiding duplicate installations.
To use Yarn workspaces, first, enable the feature in the ‘package.json‘ at the root level:
{
"name": "monorepo",
"private": true,
"workspaces": [
"packages/*"
]
}
Here, the "packages/*" pattern means that all folders inside "packages" are considered workspaces for the monorepo.
Then, add a ‘package.json‘ file in each package folder, specifying its dependencies. For common dependencies (e.g., TypeScript), specify them only in the root package.json to avoid duplicates.
2. Lerna:
Lerna is another tool for managing monorepos in JavaScript and TypeScript projects. It helps manage dependencies between packages, execute scripts across them, and publish packages to package registries like npm.
To use Lerna, first, install the Lerna CLI:
npm i -g lerna
Then, initialize Lerna in your monorepo:
lerna init
This command will create a ‘lerna.json‘ configuration file and ‘packages‘ folder for your monorepo.
Similar to Yarn workspaces, you should have a ‘package.json‘ file in each package folder. You can use ‘lerna bootstrap‘ to install dependencies for each package and symlink local packages in the monorepo.
3. TypeScript Project References:
TypeScript 3.0 introduced a new feature called "project references," which allows you to manage dependencies between TypeScript projects. This feature provides a way for TypeScript projects to depend on one another, improving build times and enforcing correct builds.
To use TypeScript project references, create a ‘tsconfig.json‘ file for each package in your monorepo and set up the dependencies using the ‘references‘ option.
For example, let’s say you have two TypeScript packages, ‘package-a‘ and ‘package-b‘, where ‘package-b‘ depends on ‘package-a‘. Your configuration files would look like this:
‘package-a/tsconfig.json‘:
{
"compilerOptions": {
...
}
}
‘package-b/tsconfig.json‘:
{
"compilerOptions": {
...
},
"references": [
{ "path": "../package-a" }
]
}
Here, the reference in ‘package-b/tsconfig.json‘ states that it depends on ‘package-a‘.
Now, to manage and build the entire monorepo, create a ‘tsconfig.json‘ at the root level:
{
"files": [],
"references": [
{ "path": "./packages/package-a" },
{ "path": "./packages/package-b" }
]
}
Finally, use the ‘tsc‘ command with the ‘–build‘ flag to build the entire monorepo, taking care of the dependencies:
tsc --build
Each approach has its benefits and use cases. Yarn workspaces and Lerna handle more than just TypeScript-specific configurations and have advanced features for dependency management and publishing. TypeScript project references focus on build dependencies and improving build times. In many cases, combining these approaches (e.g., using Yarn Workspaces or Lerna alongside TypeScript project references) results in an efficient setup for managing TypeScript monorepos.