Packaging Angular Libraries

When you want to create Angular libraries you come across a - at first sight - very simple problem: The Angular CLI currently does not support creation of Angular libraries. Only Angular applications (and all building blocks like components, services, etc.) are supported out-of-the-box. The feature for creating libraries is currently an open issue: see here or here - and it's a highly discussed topic.

Naturally, the first thing you do is spinning up your favorite search engine and start searching. Likely, you come across a Google Docs describing the Angular Package Format - which may blow your mind, since it involves some work to do all the stuff you should do for creating a full-blown Angular package.

Creating an Angular Library

After some more searching you've found posts about how to create an Angular package. Most likely, the following methods are found:

  • Linking TypeScript
  • (yo) generators
  • webpack
  • ng-packagr

And pretty sure, there are more methods than those four. Within this blog post we want to take a look at them and their - in my point of view - pros and cons.

Requirements

The requirements for creating a library are pretty simple. For sure, it should support the Angular Package Format, but there are some other requirements for working with the package as well. The following list contains features, which each method should have:

  • Support the Angular Package Format: ES2015, ES5, UMD format
  • Typings
  • Correct auto-import possibilities for IDEs like WebStorm or VS Code
  • Testing
  • AOT compatible
  • Supports scoped packages
  • Supports SCSS or LESS
  • Have a good developer and consumer story
  • Be as non-invasive as possible

The last two bullet points need some more information. Have a good developer and consumer story is, that you can develop the library in a comfortable way, either by linking it into another project or by having a playground project within the library itself. The playground project could be a simple Angular project, where all stuff is used which is developed within the package. So you don't need to link it or create a temporary package for testing your current development.

Be as non-invasive as possible: Even there's a lot of discussions going on within the linked issues, because building a library is a non-trivial task, at some point in time, the Angular CLI will (hopefully!) support creating Angular libraries. In the meantime, we have to use other options. In my opinion, those options should be as non-invasive as possible. Instead of having to change a lot of files only needed for using the method, it would be nice, that all the configuration is read from already existing files. It should not involve a lot of file editing for creating or consuming a package. The main idea is to remove the custom build as soon as the Angular CLI library support is ready.

For the following methods, let's consider we have a company called company, building an app called app (how creative I am!) and wanting to create an Angular library called core allowing them to put some reusable stuff in there for more apps. So, what you want to do is, to import stuff from core into app by using an import like import { SomeService } from '@company/core'.

Linking TypeScript

Linking TypeScript is a very simple solution for creating a package. The idea behind this option is to not compile the code directly in the core package (so no real Angular Package is created) but npm link it into the app project and let the Angular CLI compile it directly. Good thing is, there only one process running for building your app and everything what is supported by Angular CLI is supported for your package. To make the typings work correctly, you've to adjust the tsconfig.json and add the baseUrl as well as some paths information.

But be careful with this approach. It works as long as you don't need to create a real package, which is shipped to NPM or other package servers, since no real package is created at all. Well, you can publish the whole code and still link it, but that may not be the way you want to go. And, this scenario itself is officially not supported by Angular CLI as stated here and here ("ticking time bomb").

Still, I personally like and prefer using this approach, because it works very well for development. Only one build process, live-reloading works across all linked libraries, it's fast and supports everything Angular CLI supports out-of-the-box. Due to the nature of the inner caching of the Angular CLI, you sometimes have to restart the build process, which can be a drawback.

If you want to have a little demo for this, please take a look at this GitHub repository and its readme. :-)

(yo) generators

There are a lot of (yo) generators helping to create a project which enable to build it into the Angular Package Format - e.g. generator-angular2-library, generator-ngx-library or angular-librarian and much more.

Each of them provide their own unique set of features but want to help creating the Angular Package Format. Depending on your situation - e.g. if you already have a code base or starting a new one - they may provide a good starting point. But they come with the cost of mostly having a custom build process with gulp or providing a complete own CLI. It may also be hard to participate in further development of the generators, if you start adapting their code to fit your needs.

In my opinion this approach may be the most complete one (depending on which generator/CLI you're going to use), but also the one which is hard to remove when Angular CLI starts supporting the creation of libraries.

webpack

Another approach is to build your own webpack.config.js, supporting the scenario (or using a generator for that). This is basically the same, as the generator approach and could lead to a lot of work and adjustments to keep it up-to-date with Angular CLI updates, as well as implementing everything yourself which is needed. You also need to keep track of which libraries should be included in your packages and which not (e.g., peerDependencies should not get bundled into your library).

Instead of writing your own custom webpack configuration, you can eject the internal Angular CLI configuration by using ng eject. It installs some more dependencies and reveals the webpack configuration allowing to make modifications. But be careful, you're going to remove a lot of stuff, since the tools used in there, don't have library support. :-)

Using this approach opts-out of further development of the Angular CLI, which you need to track yourself, re-eject again in an empty project and transfer all changed stuff to your own configuration.

ng-packagr

Using ng-packagr is also a valid approach, which may be the most non-intrusive of all other approaches, since it's basically adding a config and a npm script and boom, everything will be done automagically. Angular Package Format, typings, inline templates, SCSS/LESS/stylus - Everything will be done for you. But that's also its current drawback. It's mostly configuration less, because there is almost nothing to configure. For example, you currently can't switch the tsconfig.json's used library es2015 to es2017, because the package does not allow it. Same for external dependencies.

If your use cases fit, ng-packagr is a wonderful easy-to-go packager.

Summary

After we've taken a look at some different approaches, you may ask: And which is the best one? Well, it depends on your situation and your use cases. Of course, if you are going to open source and publish your package into the wild, you want to have as much requirements met as possible, so everyone can consume your package. But if you are building an internal software where the packages are not published to any registry server, you may can consider other options as well. Everything which works for you is possible. Always think about the impact using one approach over another and which drawbacks you can accept and which not.

For me personally, I'm using the linked typescript approach more and more, since it's very easy to implement, to use and my IDE, WebStorm, is perfectly fine with it. IntelliSense works well, I've got all stuff Angular CLI supports, one build process and live-reloading across all linked libraries. And it's also the fastest process, since there is no need to wait for generating a whole library before it can be consumed.

So, I think, at the end of the day, using the linked typescript approach for development is quite nice and may should be combined with one of the other options for creating a real package, if you want to push them to a registry.

Is that all?

Sure not! After you're using one of the approaches for creating a package, there is so much more to do. For example, you can use compodoc for creating a documentation, use standard-version for automatically creating a CHANGELOG.md based on your git commits, or even use semantic-release to automate the whole release and publish process.

Or why not use snyk.io to scan your packages for security vulnerabilities or greenkeeper.io to keep everything updated?

Additionally, you can combine all that stuff with a CI system of your choice, to build a rock solid package and publish process.