ng-samurai — Schematics to improve tree shaking of Angular libraries

Angular schematics to split your library into multiple chunks with ng-packagr’s subentries

Angular libraries are a great way for open source projects or companies to share code across multiple applications.

Thanks to the Angular CLI, creating a library is easy. However, by default, your library may not be as tree shakable as you might think. In most cases, this is not a big deal, but sometimes, it can have a considerable impact on consumer’s performance. In other words, your library can slow the (initial load) of an application.

This happens mainly when your library includes third party libraries packaged in another format then like for example.

If you are not familiar with the points mentioned in the introduction I recommend you to check out the following article which explains such scenarios in detail.

But even in scenarios without problematic third-party imports, a split library makes sense. So why don’t we split our library?

Well, it’s it is practically not documented how to do this, and it is complicated. But the good news is, there are schematics to assist you, which I will show you in more detail throughout this blog post.

But first, let me show you the downside of single chunk libraries.

The Game of thrones wiki library

Let’s say we build a library that displays characters from Game of Thrones. The library project is generated with the CLI as a multi-project workspace and contains the following modules.

lib
|_ arya-stark
|_ arya-stark.{component, module, etc...}
|_ bran-stark
...
|_ cercei-lannister
...
|_ jaime-lannister
...
|_ john-snow
...
|_ lannisters
...
|_ robb-stark
...
|_ starks
...
|_ tyrion-lannister
...
|_ tywin-lannister
...
public-api.ts

Each character gets its folder which contains a , a , a , a file and an image of the character.

In real world applications there are better ways to deal with images, but for the purpose of this app, I want the library bundle to become big 😉.

The and the modules import other modules to display information about all members of the family.

Nothing fancy, a simple Angular library generated with the CLI.

Let’s analyze how tree shakable this library is in an application that takes advantage of lazy loading.

Analyze tree shaking for the got-wiki library

To answer this question we create a simple application that uses the library to display members of the stark family.

The GOT-wiki library used inside an application.

Our application uses a couple of Material components to display the toolbar and side navigation. It contains four lazy-loaded modules that are all accessible over the menu items in the form of lazy loaded routes.

Let’s analyze how this reflects in the bundles.

The webpack bundle analyzer nicely illustrates that the four characters are lazy-loaded — each character ends up in a separate chunk.

Each lazy-loaded feature gets split into its chunk.

That’s great. But wait, somethings wrong here. Each bundle is only around size. That’s super small, too small 🤔

Our library module includes the image of the character, which is very big. Since the lazy-loaded chunks are that small, the library module needs to be added somewhere else. But where?

In the main? No, the main bundle is at , they can not be there. But there’s another chunk called and indeed, it looks pretty big. Let’s toggle it in the side menu and have a closer look.

A chunk that contains all modules from the got-wiki library

All modules from the got-wiki library got bundled into one big chunk. Small quiz at this point, can you spot our lazy-loaded modules in the image above? 😉

They are displayed as blue squares at the top right corner.

The point here is that the whole library ends up in a dedicated chunk. Even if parts of the library are used in different lazy-loaded pieces, they can not be split apart. Means, once we load the images for one chunk, everything from the library gets loaded. Therefore, we lose almost all the advantages of tree shaking.

So — how to improve — well, ng-samurai!

Ng-Samurai — split your library into multiple subentries

has a very cool concept called subentries. Subentries are a lovely way to break libraries into multiple chunks instead of one. Doing this in the library, each character would end up in a separate fragment that can be loaded when needed.

Sounds awesome! But! Subentries are rarely documented, and therefore it can be tough to get started. You have to deal with a lot of complexity while the error messages from are not always helpful.

Common challenges are:

  • Find out how your IDE can understand subentries.
  • Find out how to adjust import paths from one chunk that imports another chunk.
  • Understand how to handle imports so that consumers of your library don’t need to adjust imports to deep imports, and your library remains tree shakable.

There are just a lot of things to it, and it can be very time consuming to get the right setup. That’s the reason why I created ng-samurai! ng-samurai is a collection of Angular schematics that does all that for you!

Ng-samurai is an Angular schematic which automatically updates your library to take advantage of subentries and improve tree shaking. Furthermore it helps you to quickly generate new subentries.

To get started with we first need to install it as a dev dependency of our project.

npm i -D ng-samurai

Once installed, we can use the schematics provided. Ng-samurai currently provides us with two schematics. and . Let’s first have a look at .

Split-lib

automatically updates your projects and splits it into multiple chunks

To use you need to run the following command from the root of our library.

ng g ng-samurai:split-lib

This command does the following changes to our project:

  • Converts each folder where it encounters a module to a subentry — it will add a (, , )
  • Export all the necessary Typescript files from the . Necessary files are , or other files. Basically, every TypeScript file expect files.
  • Update the at the root level and export all subentries.
  • Adjust the paths of your so that your IDE understands subentries.
ng-samurai:split-lib updates our library and splits it into multiple chunks

Build with subentries

Once successfully ran and updated the files we can run .

The output of a production build that creates multiple entry points

The current build log tells us that we build a single entry point for each module. We can also see this in our folder when checking the bundles.

Each module ends up as a separate fesm2015 bundle

Let’s check how the output of the webpack bundle analyzer looks after we build and install the lib and rerun the analyzer.

Lazy loaded feature module with a tree shakable library — the library code is now split into lazy-loaded chunks.

Wow! 🤩 That’s what you would expect if you talk about tree shaking, wouldn’t you?

Each lazy-loaded feature module now contains the code it needs and nothing more. Our library code now gets correctly split to the lazy-loaded bundle it is required. The code from our is now only downloaded if it is needed.

ng-samurai allows you to get huge optimizations with one command! 💪

Restrictions of ng-samurai

For ng-samurai to function appropriately, there are certain requirements your library needs to fulfill. In some cases, you might need to refactor your application before using .

Folder structure

Converting your library to subentries may also require a change of the folder structure. Each module will result in a subentry and needs its folder. Subentries can not have multiple modules.

Valid approach for ng-samurai to know how to create subentries — each module will result in a subentry
This will not work — each module will be converted to a subentry. Subentries can not contain subentries.

Circular dependencies

A subentry can use another subentry. But subentries can not work with circular dependencies.

Each file needs to belong to a module

Entry points can contain all sorts of files. needs a module-file to be present to perform the migration. The file indicates to that this code will be split into a subentry.

ng-samurai needs a module.ts file to be present to know where to create the subentry

The file doesn’t make sense. Therefore it can also be deleted after adjusted the code.

Creating new features with ng-samurai

did a fantastic job of splitting our library. But that’s not the only use case of this library. It also supports us in creating new subentries.

Even though he died early, Ned Stark is one of the coolest characters in Game of thrones. It will be a shame if our library doesn’t include him.

Let’s use command from for this.

ng g ng-samurai:generate-subentry

This command is very similar to the command of the Angular CLI. In fact, it uses the CLI command under the hood. But additionally, it also creates the , the and the with the correct content.

Thanks to Tomas Trajan for the assistance and the contributions on this feature.

Conclusion

Subentries can improve tree shakebility of your library and therefore boost the performance of your application.

To enable tree shaking, you need to take advantage of subentries. Subentries are pretty cool, but there’s a lack of documentation about the topic.

I gained experience with subentries by using it in big enterprise projects. Finding the correct setup can get very complicated, and you can lose a lot of hours on the topic.

is an Angular Schematics that automatically splits your library into multiple chunks.

🧞‍ 🙏 If you liked this post, share it and give some claps👏🏻 by clicking multiple times on the clap button on the left side.

Feel free to check out some of my other open-source libraries.

Passionate freelance frontend engineer. ❤️ Always eager to learn, share and expand knowledge.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store