
When it comes to create a new project, many decisions needs to be taken even before start typing some code. Regarding the project organization, more and more projects today choose to rely on a monorepo approach.
A monorepo is a software development strategy in which multiple projects are stored under the same repository. This approach delivers multiple benefits, some of them are:
With great power comes great challenges, one of them is related to the library versioning.
When you face with a single library it's easy to choose: you can rely on semver rules and just pick one library available in the plethora of release libraries, like Semantic Release or Commit and Tag Version.
With monorepos is not as easy as it seems, as you may decide between having multiple independent version or a single one common between everyone.
Let's pretend we have a monorepo with two libraries, @monorepo/core and @monorepo/client. The two main options available when deciding how to version multiple libraries are:
During my daily work, with my team we initially adopted the first strategy, but it was causing us a lot of troubles because it was really hard to understand time by time which version of what library should have been installed in some project. After some research, we shifted to the second strategy.
We also needed a smart tool that could help us in realizing this new approach, especially in an already existing project, so we ended up adopting Lerna, which is one of the most famous and adopted tools in the monorepo world.
The following hands-on will help understand how to solve this approach leveraging a library like Lerna without really either publishing or releasing anything. A demo project is available here and will provide an existing repository for starting using Lerna.
git clone https://github.com/federicoibba/lerna-demo-project.git
npm i
To install Lerna in the project, you just need to run:
npx lerna init
This command will:
Edit the lerna.json in order to have the following structure:
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.0.0",
"command": {
"version": {
"conventionalCommits": true
}
}
}
Then, commit the changes with, for instance:
git add . && git commit -m "build: setup lerna"
Let's pretend that we need a do an urgent fix in @monorepo/core, because the epoch was needed, not a string date.
Go to /core/index.js and change row 20, returning instead new Date().getTime(), then commit using a fix commit message:
git add core/index.js && git commit -m "fix: current date"
At this point, you may want to release the new library version. We will use two options that, for this demo project, prevent some errors and will help us focus on the versioning. Run hence:
npx lerna version --no-git-tag-version --no-push
Lerna will ask eventually if you are sure that the changes are correct, printing the future versions:
Changes:
- @monorepo/client: 3.8.18 => 3.8.19
- @monorepo/core: 1.0.3 => 3.8.19
In this case, which is the default behavior called Fixed/Locked, Lerna will see that the versions are highly different, conciliating the versions.
Let's say that the requirement is to keep the versioning different, then it's enough to just edit the lerna.json file and change the version row with independent instead of 0.0.0.
Also this time, run:
npx lerna version --no-git-tag-version --no-push
Lerna will propose a different bump strategy based on the independent key previously set, showing the following prompt:
Changes:
- @monorepo/client: 3.8.18 => 3.8.19
- @monorepo/core: 1.0.3 => 1.0.4
In this post you have briefly learned:
If you interested in the topic, I recommend you to keep reading other resources and articles that you can find in the bibliography.