Skip to main content

Develop your Angular 8+ library or application using secondary entry points, a feature that will allow breaking components in your library and better organization.

Basic requirements

npm v8.0.0

Repository

https://github.com/JeronimoTeixeira/entry-points

Creating the Angular project

For this article we will use the nx.

So let’s do it!

To create the project run the command below.

npx create-nx-workspace@latest

And answer the questions like is below.

 >  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

√ Choose your style                     · integrated
√ What to create in the new workspace   · angular
√ Repository name                       · sub-entry-points
√ Application name                      · sub-entry-points
√ Default stylesheet format             · scss
√ Enable distributed caching to make your CI faster · No

Look how cool, now you have the following folder structure.

sub-entry-points/
├── apps/
│   ├── sub-entry-points/
│   │   ├── src/
│   │   │   ├── app/
│   │   │   ├── assets/
│   │   │   ├── environments/
│   │   │   ├── favicon.ico
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   ├── polyfills.ts
│   │   │   ├── styles.scss
│   │   │   └── test-setup.ts
│   │   ├── .browserslistrc
│   │   ├── .eslintrc.json
│   │   ├── jest.config.ts
│   │   ├── project.json
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.editor.json
│   │   ├── tsconfig.json
│   │   └── tsconfig.spec.json
│   └── todos-e2e/
│       ├── src/
│       │   ├── fixtures/
│       │   │   └── example.json
│       │   ├── e2e/
│       │   │   └── app.cy.ts
│       │   └── support/
│       │       ├── app.po.ts
│       │       ├── commands.ts
│       │       └── e2e.ts
│       ├── cypress.config.ts
│       ├── project.json
│       └── tsconfig.json
├── libs/
├── tools/
├── .eslintrc.json
├── .prettierrc
├── angular.json
├── decorate-angular-cli.js
├── jest.config.ts
├── jest.preset.js
├── nx.json
├── package.json
├── README.md
└── tsconfig.base.json

Let’s focus specifically on the libs folder. It is in this folder that we will create our entry points.

But first, what is a sub entry point?

For our application, we will create a simple library that contains secondary entry points, but first, let’s understand a little of what a secondary entry point would be.

Let’s assume that some user wants to use our library in its entirety, then he will import our main module as follows.

import { EntryPointModule } from @entry-point-module

That way it will get our main package. Note that @entry-point-module is our main entry point, not a secondary entry point.

But now, in another application, another user just wants to use our feature module, so it will import as follows.

import { EntryPointTesteModule } from @entry-point-module/feature

That’s it, the user is using our secondary entry point. Here we have @entry-point-module/teste as our secondary entry point.

Creating entry points

Creating the main library

So the first step is to create the main library with the following command.

npx nx g lib <nome-lib> --publishable --importPath <import-path>

In which, we will fill in as follows

npx nx g lib entry-points --publishable --importPath @entry-points

publishable

If your intention is to distribute your library outside the monorepo, for example, if you want to publish your library somewhere to be installed with npm install, use publishable.

buildable

If your intention is only to use it in your application and not publish it, for example, if you want to use it in some monorepo, use buildable.

Perfect!! Now you have a library inside libs with the following structure.

libs/
├── entry-points/
│   ├── src/
│   ├── ├── lib/
│   ├── ├── ├── entry-points.module.ts
│   ├── ├── index.ts
|   ├── ng-package.json
└── └── ng-package.json

Now look in your tsconfig.base.json file we have your entry point within paths @entry-points.

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "module": "esnext",
    "lib": ["es2017", "dom"],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "baseUrl": ".",
    "paths": {
      "@entry-points": ["libs/entry-points/src/index.ts"]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

And also in index.ts we have the following code.

export * from './lib/entry-points.module';

It contains all the files that will be exposed when importing the @entry-points entry point. In this file we can place components, services, directives, among others, but we will use only the module to facilitate understanding.

See also the ng-package.json file, where we have referenced the index.ts export file used for the entry point @entry-points. This file is important because it is with which the Angular build recognizes the index.ts file.

{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/libs/entry-points",
  "lib": {
    "entryFile": "src/index.ts"
  }
}

Creating the first sub entry point

Use the following command to create your secondary lib, let’s call it feature.

npx nx g lib entry-points/feature --importPath @entry-points/feature

Perfect, now we have a sub entry point that will be built at the same time as the main entry point.

See that now the folder structure is as follows

libs/
├── entry-points/
│   ├── feature/
│   ├── ├── src/
│   ├── ├── ├── lib/
│   ├── ├── ├── ├── entry-points-feature.module.ts
│   ├── ├── ├── index.ts
│   ├── ├── project.json
│   ├── src/
│   ├── ├── lib/
│   ├── ├── ├── entry-points.module.ts
│   ├── ├── index.ts
|   ├── ng-package.json
└── └── project.json

It is important to understand that if you are creating the secondary entry point outside the main library, you may receive an error during build because with this method you are no longer creating a secondary entry point, but another main library with a main entry point. The purpose of the secondary entry point is to have different library modules integrated into the main library or else available at the time the main library is installed. We will understand this in the next topics.

Creating a component for your feature

Now we will create a component in the feature so that we can use it in the main library, for that use the command below.

npx nx g c component --project entry-points-feature

Now let’s expose this component in index.ts and also in the feature module as follows.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ComponentComponent } from './component/component.component';

@NgModule({
  imports: [CommonModule],
  declarations: [ComponentComponent],
  exports: [ComponentComponent]
})
export class EntryPointsFeatureModule {}
export * from './lib/entry-points-feature.module';
export * from './lib/component/component.component';

And finally, let’s import this component into our main library. Thus, within entry-points.module.ts do the following.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EntryPointsFeatureModule  } from '@entry-points/feature';

@NgModule({
  imports: [CommonModule, EntryPointsFeatureModule],
})
export class EntryPointsModule {}

Also create a component in the main library to enclose the component created within the feature.

npx nx g c component-principal --project entry-points

In the component-principal.component.html file, place the tag of the component created inside the feature as follows.

<sub-entry-point-component></sub-entry-point-component>

Build time

It’s almost done. Let’s built it!

npx nx build --project entry-points

Unfortunately the following error occurred:

Building Angular Package

 >  NX   Entry point @entry-points/feature which is required by @entry-points doesn't exists.

   Pass --verbose to see the stacktrace.


 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Ran target build for project entry-points (2s)

    ×    1/1 failed
    √    0/1 succeeded [0 read from cache]

This error occurs because a crucial file is missing to build the secondary entry points. Then go to your feature and create the ng-package.json file with the following content.

{
  "lib": {
    "entryFile": "src/index.ts"
  }
}

And let’s build again.

Building Angular Package

------------------------------------------------------------------------------
Building entry point '@entry-points/feature'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy partial compilation mode.
✔ Writing FESM bundles
✔ Built @entry-points/feature

------------------------------------------------------------------------------
Building entry point '@entry-points'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy partial compilation mode.
✔ Writing FESM bundles
✔ Copying assets
✔ Writing package manifest
✔ Built @entry-points

------------------------------------------------------------------------------
Built Angular Package
 - from: C:UsersJeronimoDesktoptestesub-entry-pointlibsentry-points
 - to:   C:UsersJeronimoDesktoptestesub-entry-pointdistlibsentry-points
------------------------------------------------------------------------------

Build at: 2022-11-18T02:35:59.607Z - Time: 2018ms


 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Successfully ran target build for project entry-points (4s)

Note that first the secondary entry point is built and finally the main library is built. If the entry point is outside the main library, the build will understand that it is a dependency and not a secondary library/entry point.

With this pattern, it is possible to reuse components, services, and directives of a certain secondary entry point in another secondary entry point or even in the main entry point.

In addition, the user using this library can choose to use only one of the secondary entry points and not the entire library.

Conclusion

Well, it’s up to you which pattern is best to use. Everything will depend on your application and what you are looking to create.

But that’s it!! Thank you for your attention!!!

Jeronimo Teixeira – BRQ System Analyst.

FALE CONOSCO