2015-12-27

In this post we are going to see how an Angular 2 component library can be built and then consumed. Check this repository for a minimal sample library and examples on how to consume it in both SystemJs and Webpack. Let's go over the following topics:

Choosing a format to publish an Angular 2 library - CommonJs

Setting up the Typescript compiler

Publish typings and source maps

exposing the library public API

How to publish component CSS

Isolating CSS via component encapsulation

Overriding CSS if needed

publishing a library to npm

Consuming a library using SystemJs

Consuming a library using Webpack

Choosing a format to publish an Angular 2 library

During the alpha stage of Angular, the Angular bundles where initially published in SystemJs format, but now they are published in CommonJs (see here). This happened for a couple of good reasons:

CommonJs is easily consumable by existing popular tools such as Browserify or Webpack

CommonJs can also be easily consumed by SystemJs

CommonJs is the node.js module format, which allows for components to be more simply used in server-side rendering

the size of CommonJs bundles is smaller (in the case of Angular the gain was around 20%)

Due to the above-mentioned reasons, the current most convenient format to publish an Angular 2 library is a set of Javascript-only files in the CommonJs module format.

What does a published library look like?

Lets take a look at what the sample library (named angular2-library-example) looks like when published to npm:

The following are the different components of the library:

components.js and components.d.ts define the public API of the library.

the lib folder contains the bulk of the library, in this case the Angular 2 component named HelloWorld

each Javascript file is published together with the Typescript type definitions and source maps

each Javascript file follows the CommonJs module format of node, exporting its dependencies via module.exports

Why publish unbundled files?

Most applications consuming Angular 2 libraries will probably have a frontend build based on some combination of gulp, Jspm, SystemJs or Webpack.

For those cases its best if the consumer of the library itself does the bundling. This ensures that bundlers like webpack will give optimal results for the end user, and that they won't throw warnings.

Should bundled and minified files also be published?

Its a good idea to also publish the library as a single concatenated file, together with a minified version of it. This will allow library consumers that don't use a frontend build to still be able to use the library.

Setting up the Typescript compiler

Since version 1.5, Typescript now allows to compile the ES6 module syntax into any module format. For example, this is the standard way of importing a component using ES6 syntax:

This can then be transformed by the compiler into different module syntaxes depending on the target module format. For example this is the output for CommonJs:

We just need to configure the Typescript compiler to output CommonJs modules. Let's go over the whole Typescript configuration of the sample library:

This config ensures the following:

emitDecoratorMetadata and experimentalDecorators enable the use of annotations such as @Component

target: 'es5' means that the output of the compilation is just plain Javascript, instead of for example ES6

module: 'commonjs' means that each file will be exported as a separate CommonJs module. for example HelloWorld.tswill be converted into an isolated module

moduleResolution: 'node' ensure that the import paths will work exactly the same way in the browser as in a node runtime

outDir is where the output of the Typescript compiler is saved

sourcemap: true triggers the generation of source maps

declaration: true triggers the generation of the typings *.d.ts files, that enable IDE Intellisense

Creating the public API of a library

The sample library only contains the HelloWorld component, importable from the components barrel. Each library should provide at least one import barrel in order to:

simplify the import of library components via IDE Intellisense

Make it clear which components of the library are meant to be used publicly, and which are just internal implementation details

Publish components via CommonJs

As we saw all components of the library will be inside the lib folder. To make them publicly visible in the angular2-library-example/components barrel, we need to create a components.js in the root of the module:

Inside the components.js barrel file, we export using the CommonJs
exports syntax any component that we want to make public. In this case only the HelloWorld component is published.

Informing Typescript of library content

The file components.js would be sufficient to use the HelloWorld component, but to enable IDE Intellisense we also need to publish
components.d.ts:

This will allow auto-completion when importing HelloWorld. Next lets see how we can publish the CSS of a component.

How to publish CSS of an Angular 2 component

Angular 2 components work by default in emulated view encapsulation mode. This means that by default, the styles of Angular 2 components are isolated and don't affect the rest of the page. Instead those styles will (in almost all cases) affect only the component itself.

How does emulated CSS encapsulation work?

Take for example the HelloWorld component:

If we run one of the examples of the sample library, we can see that the
color:blue inline style is being applied in the following way:

The value _ngcontent-rgt-2 is an automatically generated attribute that Angular assigned to the outer most element of the template:

So this is how emulated encapsulation works! Its a simple trick applied at runtime to ensure that the component styles all have at least one attribute selector. This increases a lot the specificity of the component styles, making it unlikely that they ever get overridden.

How to override component CSS if needed

Although the style of a component is well isolated, it can still be easily overridden if necessary. For that, we just need to add an attribute to the body of the page:

The name of the attribute can be anything. No value is needed and the name override makes it apparent what its being used for. To override component styles, we can then do the following:

As we can see that although the component styles have a high specificity, they can still be easily overridden if its needed - and there is always some corner case for that.

How to publish component CSS then?

The current simplest way to write component styles is inline as shown above. Here are several reasons for this:

writing the styles and templates inline encourages the components not to grow too large

if the CSS (or the template) starts to grow big, its a sign that the component should be split in several components

its very convenient to have everything related to the component in a single file (like in React)

the Angular 2 template transforms mechanism is not yet ready (its scheduled for final). But when this is ready, templates and CSS will likely be inlined everywhere via plugins, like the case of templates and the various template cache plugins in Angular 1

The convenience of distribution, combined with the good isolation and the ease of override make the combination of inline styles and emulated encapsulation a good solution for publishing component CSS.

Publishing a library to npm

Once the library is built in the lib folder, it's time to distribute it. You probably have configured .gitignore to exclude the lib folder. But npm uses .gitignore by default, so the lib folder won't be published!

We need to start by creating an .npmignore file that says what should be ignored when publishing to npm:

In this case, all folders where ignored except the lib folder.

Creating a build script

In the case of a component library, we can probably keep the build very simple. Using npm scripts is likely sufficient:

This script can be run using npm run script, and it creates a lib folder ready to publish. Tools like webpack can also be used effectively via npm scripts.

If the approach becomes cumbersome, then its better to introduce gulp. But for most components, probably npm scripts will be sufficient.

npm semantic versioning

When publishing new versions of the library to npm, its important to use the built-in npm version command line tool according to semantic versioning:

This tool will update the package.json version, and commit the change to git.The convention for versioning is the following:

major: breaking changes

minor: new features, backwards compatible to current major

patch: no new features, only bug fixes (and still backwards compatible)

Publishing to npm

In order to publish to npm, first create an account in npmjs.com.

Then setup your username in password in your local npm:

When this is done, you can simply do:

This is the essential of how to publish component published to npm. Its important to follow semantic versioning to avoid breaking the builds of library users.

How to consume an Angular 2 library

Many library consumers will probably be using a module loader and a package manager.

Lets see how the sample library can be consumed using the two most commonly used tools: SystemJs and Webpack. In both cases, the first step is to install the library using npm:

Consuming a library using SystemJs

See here for an example of how to load the sample library using SystemJs

SystemJs is the most used module loader in many of the Angular 2 examples currently available. See this post for an introduction to SystemJs and the associated Jspm package manager.

Ideally we would install Angular 2 with one command via jspm:
jspm install angular2.

But this is currently not working, see for example this issue. What currently works is to load Angular 2 with some script tags:

Then import any libraries like the sample library via System.config:

And then bootstrap the app:

A good place to keep track of what is currently possible with SystemJs is to check the scaffolding of angular-cli here over time. The issue with Jspm will probably be fixed soon.

Consuming a library using Webpack

Here is an example of how to consume the sample library using Webpack

The Html needed for bootstrapping an Angular 2 app is very simple:

In the case of the sample library example, the bundle file does not exist in the file system, its in memory in the webpack-dev-server tool.

This bundle is created according to the following webpack configuration:

This configuration does the following:

sets up a typescript loader named ts-loader, that allows for webpack to compile typescript files while loading them

activates source maps

defines the entry point of the application (app.ts)

One note on using webpack to import a local version of a library using npm link: You will likelly run into cannot find module issues. Check here for a solution.

Conclusions

With the set of practices presented above, its definitely possible to publish an Angular 2 library today. Check some early examples of libraries that where the basis for this post:

ng2-pagination by @@michlbrmly - an Angular 2 port of the Angular 1 paginate directive

ng2-translate by @OCombe - An implementation of Angular 1 ng-translate, for internationalizing Angular 2 applications

The best practices for publishing libraries will probably evolve over time, its best to check angular-cli from time to time, to keep track of best practices and see what scaffolding is available.

References

How to publish a library for Angular 2 on npm from @OCombe

Show more