MonoRepo + Angular Packaged Libs :: You Can Have Your Cake and Eat It Too!!

You can have a MonoRepo and Packaged Angular Libraries at the same time!!!

I finally had some time to check out the NRWL.io Nx Workspace tool to create an Nx Workspace (MonoRepo…See article by Alex Eagle) and the ng-packagr tool to package libraries using the Angular Package Format version 6. Tools and process: a good combination.

  1. Use a simplified workflow to allow consuming applications to reference libraries via @scope.
  2. To allow for the custom libraries to be packaged using Angular Package Format conventions via ng-packagr.
  3. Use semantic versioning with the ability to publish to NPM.
  4. The ability to consume the NPM packaged versions for application deployments.
npm install -g @nrwl/schematics
npm install -g @nrwl/nx
EX: create-nx-workspace buildmotion-angular-workspace --npm-scope=buildmotion
create-nx-workspace <YOUR-WORKSPACE-NAME> --npm-scope=<YOUR-SCOPE-NAME>
EX: ng generate app angularlicious-web
ng generate app <CONSUMING-NG-APP-NAME>
create apps/angularlicious-web/e2e/app.e2e-spec.ts (282 bytes)
create apps/angularlicious-web/e2e/app.po.ts (201 bytes)
create apps/angularlicious-web/e2e/tsconfig.e2e.json (404 bytes)
create apps/angularlicious-web/src/assets/nx-logo.png (71592 bytes)
create apps/angularlicious-web/src/assets/.gitkeep (0 bytes)
create apps/angularlicious-web/src/environments/environment.prod.ts (51 bytes)
create apps/angularlicious-web/src/environments/environment.ts (387 bytes)
create apps/angularlicious-web/src/favicon.ico (5430 bytes)
create apps/angularlicious-web/src/index.html (303 bytes)
create apps/angularlicious-web/src/main.ts (370 bytes)
create apps/angularlicious-web/src/polyfills.ts (2667 bytes)
create apps/angularlicious-web/src/styles.css (80 bytes)
create apps/angularlicious-web/src/tsconfig.app.json (307 bytes)
create apps/angularlicious-web/src/app/app.module.ts (342 bytes)
create apps/angularlicious-web/src/app/app.component.html (520 bytes)
create apps/angularlicious-web/src/app/app.component.spec.ts (607 bytes)
create apps/angularlicious-web/src/app/app.component.ts (258 bytes)
create apps/angularlicious-web/src/app/app.component.css (0 bytes)
update .angular-cli.json (1705 bytes)

Application Project Files

Notice that the new CLI project doesn’t have all of the project-based files. The project-based files are shared by all CLI projects in the apps folder. They are located in the root of the workspace. This simplifies things a bit.

.angular-cli.json

The .angular-cli.json file is updated with (2) new lint definitions.

{
"project": "apps/angularlicious-web/src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "apps/angularlicious-web/e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"

Custom Library

Before, I move any of my existing CLI web projects. I’m going to add a custom library to the libs folder to see the Workspace changes. The simplest library/module I have is my logging module. I'll start with this.

Ex: ng generate lib logging --parentModule=apps/angularlicious-web/src/app/app.module.ts
ng generate lib <NAME-OF-LIB> --parentModule=apps/<NAME-OF-CONSUMING-APP>/src/app/app.module.ts
Note: If your library requires routing, use:
ng generate lib mymodule --routing
create libs/buildmotion-logging/index.ts (77 bytes)
create libs/buildmotion-logging/src/logging.module.spec.ts (215 bytes)
create libs/buildmotion-logging/src/logging.module.ts (174 bytes)
update .angular-cli.json (1873 bytes)
{
"name": "logging",
"root": "libs/logging/src",
"test": "../../../test.js",
"appRoot": "",
"tags": []
}
export { BuildMotionLoggingModule } from './src/logging.module';

Using the Custom Library

You can now import the new BuildMotionLoggingModule module into the consumer application.

import { BuildMotionLoggingModule } from '@buildmotion/logging';

Legacy Workflow

Note: You no longer have to do an [npm install] for your custom package. It is referenced using the @buildmotion scope from within the Nx Workspace. This is a nice benefit of the Nx Workspace — the development workflow is now much faster. Before, you would have to:

  1. update the version of the library (semantic versioning)
  2. compile/build
  3. publish to a package manager (or local private repository).
  4. update the package version dependency in the consuming application.
  5. test and verify the changes work
  6. repeat if there are any issues/problems and/or updates to the shared library.

Nx Workspace Workflow

The Nx Workspace workflow is much more streamlined and efficient for development of custom/shared libraries with the consuming applications.

  1. test and verify the changes work in the consuming application
  2. repeat if there are any issues/problems and/or updates to the shared library.

Package Your Libraries for Distribution

So, we now have a MonoRepo. I wasn't aware until recently of the many advantages of a MonoRepo - but, there are also a few drawbacks. It appears that the default configuration of the Nx Workspace is not setup to publish your libraries as packages. One of the reasons I'm setting up this workspace is to learn how this can either benefit or hinder my development team. A MonoRepo may not be suitable for every solution. However, having another tool and development workflow that can simplify things and still allowing for publishing custom libraries is pretty nice.

npm install --save-dev ng-packagr

Configure ng-packagr

Add a new package.json file to the root of the target lib. This is where we will define the ng-packagr configuration for the build and destination location. More information.

  • name: the name of the library.
  • version: the semantic version of the library. Use npm version major|minor|patch commands to update the version of the library.
  • ngPackage: an object value for:
  • lib: an object value that contains the entryFile to indicate the entry file of the library (contains the manifest of exported items)
  • dest: use to indicate the destination of the packaged output for the library.
  • repository: provides the source code repository for the package.
  • keywords: used by the package manager
  • license: use to indicate the license type.
  • peerDependencies: used by Angular consumers of the package to determine required peer dependencies.
npm run build:libs
Output from ng-packagr to dist folder.
Library Packaged for Angular (esm5), UMD, and esm2015.

Workspace Configuration package.json

Following the tenents of MonoRepo, we have a single-source of truth for the definitions and build scripts for the workspace. We will update the package.json in the root of the workspace to include (2) new scripts that target the build of our custom libs.

"build:libs": "npm run build:buildmotion-logging",
"build:buildmotion-logging": "ng-packagr -p libs/logging/package.json",
"build:libs": "npm run build:buildmotion-logging && npm run build:lib-this && npm run build:lib-that ",
"build:buildmotion-logging": "ng-packagr -p libs/buildmotion-logging/package.json",
"build:lib-this": "ng-packagr -p libs/lib-this/package.json",
"build:lib-that": "ng-packagr -p libs/lib-that/package.json",

Logging

I updated the module with a service, a configuration (future), and an enum to indicate the Severity of the log item. This will provide some basic functionality that we can use from any of the workspace apps.

Consume the Tacos (Lib)!

Since, we now have a library — we can start to consume it. The target lib is a logging service. We can add the LoggingServiceto the providers collection of the AppModule.

providers: [
LoggingService
]

Run the Application

You can create a launch profile - and update the port to use 4200.

ng serve

Conclusion

I think I’m having a cake and eat it too moment. Wow, I can have a MonoRepo Workspace environment with multiple applications consuming multiple libraries. I can also publish those packages to NPM with semantic versioning — so that they can be shared. The production environment configuration of the application can be configured to consume the distributed version of the module package.

Angular CLI App consuming Nx Module

I love tacos, code, jazz, my husky and maybe 3 people in this world...and of course: Angular. Angularlicious podcast — more info at www.AngularArchitecture.com