Angular 14 monorepo with lerna, npm workspaces and Git
In this tutorial with the latest version, we'll see by example how to create an angular 14 monorepo project containing two packages: a shared library and a demo application using tools like lerna, git and npm workspaces.
Lerna and npm workspaces are tools that help developers develop libraries and applications in a single repo (also known as a monorepo) without having to publish to npm or other registries in the development phase of a project. By accessing shared components and libraries locally, we can complete the development (such as coding, testing and debuging) cycles considerably faster.
Setting up a monorepo for angular 14 with lerna and npm workspaces
If this is your first time using Angular 14, you'll need to install it globally. Open a command-line interface and run the following command:
npm install --global @angular/cli
Next, let’s create an empty Angular workspace as follows:
ng new angularmonorepo --create-application=false
I'm not interested in creating an initial application and so we used the —create-application
option with the ng new
command. When set to false, an empty workspace with no first app is created.
Next, install lerna as a development dependency in your Angular 14 project using the following commands:
cd angularmonorepo/
npm install lerna --save-dev
The command added lerna to devDependencies
in package.json
For incorporating lerna into our project to assist us with library management, we simply need to run lerna init
which generates a new lerna repository. We’ll be using independent mode with the --independent
option to allow package versions to be incremented regardless of one another.
Then, proceed to set up a monorepo for your Angular workspace with lerna as follows:
lerna init --independent // initialize lerna monorepo
We set the version to independent so we can manage the packages’ versions and publish them independently.
Once the startup process has been completed, you will be presented with the following output:
info cli using local version of lerna
lerna notice cli v4.0.0
lerna info Updating package.json
lerna info Creating lerna.json
lerna info Creating packages directory
lerna success Initialized Lerna files
Notice that lerna has created a lerna.json
file and a packages/
folder and only update the existing package.json
rather than creating a new one.
By default, lerna points its packages to the packages
folder.
We’ll not be using the default lerna’ packages/
folder since Angular uses the projects directory for applications and libraries, therefore, we need to tell lerna about that. Open the lerna.json
file and update it as follows:
{
"packages": [
"projects/*"
],
"version": "independent"
}
Then you can simply delete the packages/
folder from your Angular 14 workspace:
rm -r packages/
Next, open the package.json
file and add the following key to use npm workspaces:
"workspaces":["projects/*"]
Next, generate a library using Angular CLI v14 as follows:
ng g lib shared
This library will be generated inside the projects/
folder by Angular CLI which we configured as a workspace for both lerna and npm.
Then, create an application using Angular 14 CLI as follows:
ng g app demoapp
You’ll be prompted as follows:
- ? Would you like to add Angular routing? Yes
- ? Which stylesheet format would you like to use? SCSS
Following that, create a package.json file inside the application’s root folder and add the following contents:
{
"name": "demoapp",
"version": "0.0.1",
"scripts": {
"start": "ng serve demoapp",
"build": "ng build demoapp",
"test": "ng test demoapp",
"lint": "ng lint demoapp"
}
}
Next, open the project’s level package.json
file and update the scripts to use lerna:
"scripts": {
"ng": "ng",
"start": "lerna run start --scope=demoapp --stream",
"build": "lerna run build --scope=demoapp,shared --stream",
"buildlib": "lerna run build --scope=shared --stream",
"watch": "ng build --watch --configuration development",
"test": "lerna run test --scope=demoapp,shared --stream"
}
Save the root package.json
file and build the shared library by running the following command:
npm run buildlib
This will display the following output on the screen:
> [email protected] buildlib
> lerna run build --scope=shared --stream
lerna notice cli v4.0.0
lerna info versioning independent
notice filter including "shared"
info filter [ 'shared' ]
lerna success run No packages found with the lifecycle script 'build'
Let’s solve this by adding a build script to the package.json
file of the shared library as follows:
"scripts":{
"build": "ng-packagr"
}
Save the file and re-run the npm run buildlib
command. You should get a similar truncated output:
shared: Built Angular Package
shared: - from: ..../tutorials/angularmonorepo/projects/shared
shared: - to: ..../tutorials/angularmonorepo/dist/shared
shared: -------------------------------------------------------------------
shared: Build at: 2022-04-11T03:43:30.517Z - Time: 3114ms
lerna success run Ran npm script 'build' in 1 package in 4.1s:
lerna success - shared
Next, let’s add the built shared library as a dependency in our app by running the following lerna command:
lerna add shared
You should get a similar output:
info cli using local version of lerna
lerna notice cli v4.0.0
lerna info versioning independent
lerna info Adding shared in 1 package
lerna info Bootstrapping 2 packages
lerna info Symlinking packages and binaries
lerna success Bootstrapped 2 packages
These are the contents of the demoapp package.json
file after running the previous command:
{
"name": "demoapp",
"version": "0.0.1",
"scripts": {
"start": "ng serve demoapp",
"build": "ng build demoapp",
"test": "ng test demoapp",
"lint": "ng lint demoapp"
},
"dependencies": {
"shared": "^0.0.1"
}
}
Notice that shared has been added to the application dependencies.
You can test if everything works by going to the demoapp/src/app/app.module.ts
file and import symbols from the shared module as follows:
// [...]
import { SharedModule, SharedService } from 'shared';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
SharedModule
],
providers: [SharedService],
bootstrap: [AppComponent]
})
export class AppModule { }
Next, run your angular 14 demo application using the following command:
npm start
It should show the following truncated output and build then serve the app:
> lerna run start --scope=demoapp --stream
lerna notice cli v4.0.0
lerna info versioning independent
notice filter including "demoapp"
info filter [ 'demoapp' ]
lerna info Executing command in 1 package: "npm run start"
[...]
demoapp: ** Angular Live Development Server is listening on localhost:4200,
open your browser on http://localhost:4200/ **
demoapp: ✔ Compiled successfully.
Conclusion
You could ask why using both lerna and NPM Workspaces. What functions do each of them play in our development process? In a nutshell, the features provided by the two utilities are quite similar. Both lerna and npm workspaces are capable of reading package.json
files and configuring the required symlinks but lerna add a few commands that make things a lot easier.
Both lerna and npm workspaces use a packages folder in your repository. A package may be independently initialized, versioned, and published. The example 14 monorepo shown above contains two packages:
- A shared library: This may contain code that designed be reused.
- A demo app: This is an angular 14 application that may import and use the code exported from the shared library.
In the next tutorial, we’ll learn how to streamline our angular library release with commit conventions and commitizen.
-
Date: