Lerna and NX are two great options for monorepos. However, they have different use cases. While Lerna is focused on linking package from the same project, NX is focuses in managing development workflow for mutiple packages.
Lerna
The Good: Is a better fit for open source project with muntiple packages since you can easily publish related packages while keeping their versions synchronized.
What is missing: No tooling for package boilerplate. I you use Typescript for example, you handle boilerplate and build process yourself.
NX
The Good: Is best for development workflows. Multiple plugins and boilerplate are avialble for various frameworks, React, Angular, VueJS, NestJS, Express etc. Out of the box commands for
- Generating new project (using plugins of course) for your target framework (NestJs, Angular, React etc)
- Building projects
- Testing projects
Whats is not so good?
- Single
package.json
file for all the projects. Meaning that all the project (or packages) have to use the same version of the same package. - While using the same package.json has some advantages it also has challenges. Front-end App benefit from tree shaking process that removes unused modules. However, Back-End Apps will be bundled with unused packages causing a bloat in App artifacts (with Docker for example).
Why not use both?
Since none of them is perfect why not use both and let them complete each other's weaknesses. We get the best of both worlds. Follow the steps below to use them together.
1. Create an NX Workspace
npx create-nx-workspace
2. Install Lerna
npm install lerna --save-dev
3. Create a lerna.json file
{
"packages": [
"libs/*"
],
"version": "independent",
"command": {
"publish": {
"conventionalCommits": true,
"yes": true
},
"version": {
"message": "chore(release): publish"
}
}
}
4. Conventional commits
When using conventiinal commits you can
- reinforce a convention for commit messages using conventional commits
- automatically update package versions (patch,minor, major) based on commit messages.
- automatically generate changelogs
4.1 Install commitizen
This package helps us to commit the message in the conventional-commit format
npm install commitizen -g
4.2 Initialize project
Initialize your project to use the cz-conventional-changelog
adapter
commitizen init cz-conventional-changelog --save-dev
or with yarn
commitizen init cz-conventional-changelog --yarn --dev
The above command will
- Installs the cz-conventional-changelog adapter npm module
- Save it to package.json’s dependencies or devDependencies
- Adds the config.commitizen key to the root of your package.json as shown here:
...
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
...
You can now run git cz
to commit messages.
5. Set publishConfig
Most if not all NX project are TypeScript project that are build. Lerna is not aware of this. For each package update the package.json to point to the build directory. If your package is named mypakage
, you should update the package.json as below
"publishConfig": {
"access": "public",
"directory": "../../dist/libs/mypackage"
}
6. Build package with NX after version updates by lerna
When you are ready to publish you pakage you run `lerna publish`. This command does 2 things
1. There is a version bump by leran using `npm version`. This update the package.json version (and package-lock.json) if there is.
2. The changed packages are published.
Before publishing the package we need to make sure that the built source code has the most up to date changes including
- The source built code
- The updated version of the package.json file (and package-lock.json)
To do this we add a script to the package.json
"version": "npm run affected:build"
This script makes sure that after updates the package.json file, both the built source code and package.json (+ package-lock.json) are updated in the dist
folder before publishing.
Voila!
Now you get the best of both worlds. You can use NX for development workflow and Lerna for synchronising package version and publishing the related packages.