DEV Community

Cover image for Why and how to use secondary entrypoints in your Angular Libraries in Nx
kauppfbi
kauppfbi

Posted on

Why and how to use secondary entrypoints in your Angular Libraries in Nx

Photo by Sangga Rima Roman Selia on Unsplash

Nx Devtools recently released version 13.1, which came with a very handy generator for Angular libraries, which creates secondary entrypoints for your Angular Libraries with ease. In this blog post we will cover why and how we should make use of this new function.

Why secondary entrypoints make sense?

By adding secondary entrypoints, we basically split our Angular libraries into multiple chunks, just like Angular Material does.
A Example:

// One entrypoint: 
import { MatButtonModule, MatCardModule } from '@angular/material';

// With secondary entrypoints:
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
Enter fullscreen mode Exit fullscreen mode

The idea behind it is not new at all, this option already exists since the beginning phase of ng-packagr. By leveraging secondary entrypoints, we aim better architecture, support of treeshaking, smaller bundle sizes and therefore faster applications.

Demo 👀

To demonstrate the impact, I prepared a little example:
Therefore I created a small greeter library and an a simple test-app, which just consumes one component out of this greeter library.

Here is the greeter library, which contains a simple greeter and a fancy greeter:

// simple-greeter.component.ts
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';

@Component({
  selector: 'kauppfbi-blogs-simple-greeter',
  template: ` <p>Nice to meet you, {{ name }}!</p> `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SimpleGreeterComponent {
  @Input() name!: string;
}

// fancy-greeter.component.ts
...
import moment from 'moment';

@Component({
  selector: 'kauppfbi-blogs-fancy-greeter',
  template: `
    <p>Nice to meet you, {{ name }}!</p>
    <p>Today is the {{ time }}</p>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FancyGreeterComponent implements OnInit {
  time = '';
  @Input() name!: string;

  ngOnInit(): void {
    this.time = moment().format('DD.MM.YYYY');
  }
}
Enter fullscreen mode Exit fullscreen mode

Both are exported in the index.ts file (public api) of the library:

export * from './lib/simple-greeter/simple-greeter.module';
export * from './lib/simple-greeter/simple-greeter.component';

export * from './lib/fancy-greeter/fancy-greeter.module';
export * from './lib/fancy-greeter/fancy-greeter.component';
Enter fullscreen mode Exit fullscreen mode

You probably noticed, that the fancy greeter imports moment... you already know, where this leads to.

Our test-app is also kept simple:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SimpleGreeterModule } from '@kauppfbi-blogs/greeter';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, SimpleGreeterModule],
  bootstrap: [AppComponent],
})
export class AppModule {}

// app.component.ts
@Component({
  selector: 'kauppfbi-blogs-root',
  template: `<kauppfbi-blogs-simple-greeter [name]="name"></kauppfbi-blogs-simple-greeter>`,
})
export class AppComponent {
  name = 'Duke';
}
Enter fullscreen mode Exit fullscreen mode

To complete the demonstration, we will use the webpack-bundle-analyzer to inspect the production bundle of the app:

# build app with production configuration and generate bundle statistics
npx nx test-app:build:production --stats-json
# inspect the bundle
npx webpack-bundle-analyzer dist/apps/test-app/stats.json
Enter fullscreen mode Exit fullscreen mode

This will open up a new webpage containing the chunks of our app:
Webpack Bundle Analyzer unoptimized
As you can see, our bundle consist largely of moment even though we did not consume the fancy greeter, but the simple greeter. 🐵

How to use the new generator?

Now as we know, why it makes sense to use secondary entrypoints in Angular Libraries, we will look into how to use them.

As it is just one command, it is pretty simple:

npx nx generate @nrwl/angular:library-secondary-entry-point \
--name=my-secondary-entrypoint \
--library=my-library
Enter fullscreen mode Exit fullscreen mode

or with the Nx Console:
Nx Console

The command will scaffold a new folder with the name of your entrypint containing a src folder with a Readme, a package.json and a module exported by a new index.ts file. In addition, it will adjust the lintFilePattern of the entire library project and it will create the correct pathMapping in the tsconfig.base.json.

Now you can either move existing modules there or create new ones.
In our use case, it was a matter of two minutes to shift the code.

Let's analyze the result:
Webpack Bundle Analyzer optimized
First of all, we can see that the bundle size was decreased significantly. Okay, this was just a demo and I wouldn't recommend to use moment nowadays at all. But, more importantly we verified that we only load necessary code and we are able to support proper treeshaking with our solution!

That's it already, happy coding! 🚀

GitHub Repository

All the code is available in a public repository on Github. You can find the greeter library here and the test-app here.

Further Links 🔗

Checkout the following blog posts to learn more about seondary entrypoints:

Top comments (0)