29

How to create a fully tree shakable icon library in Angular

 4 years ago
source link: https://medium.com/angular-in-depth/how-to-create-a-fully-tree-shakable-icon-library-in-angular-c5488cf9cd76
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to create a fully tree shakable icon library in Angular

Image for post
Image for post
Image for post
Image for post

AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

Nearly every SPA uses icons. Often, we use icons delivered by a component library, such as Material, or we take icons from a dedicated icon library like font-awesome. In some cases, however, the icons need to match our brand. Therefore, we need to provide a custom icon library.

If you create an icon library, performance is something to consider. You don’t want to deliver a set of 300 icons to the user if they only are going to display two icons. Right?

Previously, I wrote an article about how to create an icon library in Angular. I recommend you check out this article because it explains a lot of concepts and best practices when it comes to creating an Icon library. It will also help you better understand the things explained in this article.

Too lazy to read the article? Here’s a quick summary:

  • We should prefer SVG icons over fonts because they offer more features.
  • Since SVG icons are just code, they are bundled into our JavaScript files.
  • An icon registry is a clean way to make icons tree shakable.
  • svg-to-ts is a nice helper tool to transform SVG icons into code.

Why another article❓

Recently I had the chance to build an icon library for a big client. During the process, I gained some new valuable insights which are worthy to share.

Let’s start by having a look at what can be improved in the current approach?

Throughout this blog post, we are going to use those awesome Icons made by Freepik from www.flaticon.com

Different levels of tree shaking 🌲

My previous article describes how to develop a tree shakeable icon library, however, they are not fully tree shakable. Fully tree shakable? 🤔

Never heard of “fully tree shakable”? Well, that’s not surprising, it’s a term I invented here (it’s not an official term 😉). What do I mean by that?

Treeshakable icons so far

Image for post
Image for post
Tree shakable icons: Only the used icons end up in the resulting bundle

So far we created an icon library containing 5 icons. With a non-tree shakable library, all 5 icons result in the final bundle. With a tree shakable library, on the other side, only the used icons end up in the final bundle.

So far so good. The question now is, what happens if we use Icons inside lazy-loaded feature modules? 🤔

Lazy loading is one of the most important Angular features to boost initial start time of a web application. Lazy-loaded code ends up in a separate JavaScript bundle so that it is only loaded once it is needed. Therefore, the size of the main bundle decreases and our application loads faster.

Let’s say we have an application that shows professions. The application contains two lazy loaded routes; /artist and /cook. Each route contains a description of the profession and uses the dinosaur icons to illustrate the profession.

Image for post
Image for post
Professions application that uses the dinosaur icons to illustrate a profession

Since we are interested in performance its best practice to analyze our bundles using the webpack-bundle-analyzer.

Image for post
Image for post
All Icons get split into a separate common chunk. Lazy loaded feature modules get split into their own chunk

All of our icons get split into a separate chunk. This means when we navigate to /cook we load all icons, even though we only display the cook dinosaur icon.

In the example above, we imported all icons in lazy loaded modules. If an icon is included in an eagerly loaded module, all icons will end up in the main chunk. Having icons in the main bundle decreases the initial load time of our application.

Image for post
Image for post
If an icon is eagerly loaded, all icons end up in the main chunk

Wouldn’t it be great if the icons would also be split to the lazy-loaded chunks? This is what I mean by the term “fully tree shakable” icons.

☝️ Fully tree shakable are icons that are split to the chunk they are uesd in.

Image for post
Image for post

How to create a fully tree shakable icon library?

To create such a fully tree shakable icon library we are going to refactor the creation and generation of the icons described in the previous blogpost. However, we are going to keep the icon registry and the component.

In case you still didn’t read the previous blog post, here is the component and the registry code as a refresher.

Image for post
Image for post
dinosaur-icons-registry.service.ts
Image for post
Image for post
dinosaur-icon.component.ts

The idea here is that once you want to use an icon, you add it to the registry. Then, you pass the name of the icon to the DinosaurIconComponent which then uses the registry to retrieve the icons data and to append it as an SVG element.

If you want to enable tree shaking you need to be explicit about your intends.

We need to rethink how we convert the icons❗

So far, we converted the SVG icons to a single TypeScript file which during the build process, is then transpiled to JavaScript. Since it’s one JavaScript file with exported constants, it’s content is tree shakable but not fully tree shakable. 😉

To be fully tree shakable, each icon needs to end up as a single piece of JavaScript.

Sounds good, how do we get there?

The good news is, we extended the svg-to-ts generator so that it does all the heavy lifting for you. We just have to configure it accordingly. Let me show you how.

In case you haven’t installed svg-to-ts yet, just go ahead and use the following command to install it as a dev dependency npm i -D svg-to-ts.

We will configure svg-to-ts given the following file structure:

dinosaur-icons
|_ icons
|_ svg-icons
|_ artist.svg
|_ birtday.svg
|_ chef.svg
|_ sleep.svg
|_ space.svg
|_ src
|_ lib
|_ dinosaur-icon.component.ts
|_ dinosaur-icons-registry.service.ts
|_ dinosuar-icons.module.ts

svg-to-ts has multiple ways to be configured. You can either pass all the configuration arguments over the command line, add a svg-to-ts key with a configuration object to your package.json or you can add a .svg-to-tsrc file on the root level of your project.

I personally prefer to configure it by using a configuration object in my package.json.

"svg-to-ts": {
"srcFiles": [
"./projects/dinosaur-icons/icons/**/*.svg"
],
"outputDirectory": "./projects/dinosaur-icons/icons",
"interfaceName": "DinosaurIcon",
"typeName": "dinosaurIcon",
"prefix": "dinosaurIcon",
"optimizeForLazyLoading": true,
"modelFileName": "dinosaur-icon.model",
"additionalModelFile": "./projects/dinosaur-icons/src/lib",
"compileSources": true
}

Let’s take a quick walk through all those properties and their meaning.

  • srcFiles: Regex that matches the source files (SVG icons).
  • outputDirectory: the directory where we want to create the icons.
  • interfaceName: Name of the generated interface for the icons.
  • typeName: Name of the generated type for the icons.
  • prefix: Prefix of the icons and the generated icon files.
  • optimizeForLazyLoading: Flag to indicate if we want to optimize our library for lazy loading (making it fully tree shakable)
  • modelFileName: Name of the generated model file (File that contains the interface and the types.
  • additionalModelFile: If we want to create an additional model file. This file is useful to improve the typings.
  • compileSources: Flag to control if the SVG‘s should be compiled by the TypeScript compiler or not. If set to false svg-to-ts will output .ts files. If set to true svg-to-ts will output .d.ts files and .js files.

After we configured svg-to-ts we can go ahead and add a generate-icons script to our package.json.

"generate-icons": "svg-to-ts"

This script runs svg-to-ts with the configuration of our package.json and generates the following files.

Image for post
Image for post
Fils generated by svg-to-ts

All the icons are optimized, transformed to TypeScript and transpiled to JavaScript files. Each icon becomes a JavaScript file. Furthermore, svg-to-ts also generates the correct TypeScript declaration files and a dinosaur-icon.model.d.ts file to improve typings.

Additionally, we also specify the path for an additionalModelFile. This file is generated next to our DinosaurIconComponent and the DinosaurIconRegistry. Generating this file a second time is necessary because we want to have type safety and the icon file is currently not included in our library build.

Including the generated icons

To include the generated icons in our library we simply copy them over into the dist folder. There are several tools to copy files, I personally like cpx.

npm i -D cpx

Once installed, we add the following post-build script in our package.json.

"postbuild": "cpx 'projects/dinosaur-icons/icons/**/*.{d.ts,js}' dist/dinosaur-icons/icons",

This script copies all the d.ts and js files to the dist/dinosaur-icons/icons folder after our library build which results in the following dist folder.

Image for post
Image for post

That’s it, at this point we created a fully tree shakable icon library.

Usage of our icon library

Let’s now explore how we can use our icon library in our profession’s application.

Image for post
Image for post
Import the DinosaurIconsModule and add the dinosaurIconArtist icon to the DinosaurIconsRegistry

We import the DinosaurIconsModule from dinosaur-icons and add it to the imports array of our ArtistModule. With this, we can now use the dinosaur-icon component in our templates.

To display the artist icon we first need to import it from dinosaur-icons/icons (our icons folder) and add it to the DinosaurIconsRegistry.

At this point we are ready, to use the artist icon in our template.

Image for post
Image for post
Use the artist icon in our template

The cool thing here is that due to the typings generated by svg-to-ts, modern IDE’s give us a suggestion of the possible icon names and complain if we use a wrong icon name.

Cool, but how does our bundle look like? Are our icons really fully tree shakable. Yes, they are!

Image for post
Image for post
Icons split on lazy-loaded modules

We can see that the icons do not land up in a common chunk, nor in the main bundle. They now end up in the bundle they are really used.

Here’s another image of the bundle analyzer that additionally shows the main bundle.

Image for post
Image for post
Fully tree shakable icons decrease the sice of our main bundle

Even if an icon is included in an eagerly loaded module, the rest of the icons is bundled in a lazy loaded chunk. If you have a huge icon library this decreases the size of your main bundle and improves initial loading time.

svg-to-ts gives you even more power 🦍

svg-to-ts is highly configurable and therefore very flexible. It’s up to you to decide how you want to build your icon library.

Maybe, simple tree shaking is enough for your use case, then set optimizeForLazyLoading to false. Maybe you don’t want to compile the icons because you are only interested in the TypeScript files, then set compileSources to false. Find the correct configuration for your use case.

In our current project as an example, the same icon library is used by multiple component libraries. Therefore we decided to extract the icons to a separate package. This has the advantage that the icons can be used by multiple registries and we only have to make one release if the icons change.

In this approach, we only generated the TypeScript sources and compiled them ourselves as part of our build process.

Conclusion

Tree shaking is a common term, however, there are multiple levels of tree shaking. While creating an SVG icon library in a tree shakable way may be easy, creating it in a fully tree shakable way can be a challenging task. Trust me, we spend quite some time on it.

svg-to-ts is a very helpful tool that does all the heavy lifting for you when you want to generate your own icon library. It can be used with multiple frameworks. It accepts various configurations and therefore supports multiple ways of building an icon library.

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

Claps help other people to discover content and motivate me to write more 😉

Feel free to check out some of my other articles about front-end development.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK