Converting your Angular CLI application into a NPM Module: Part Two

In the second part of our NPM & Angular CLI series we discuss how we turned our internal application into an npm package and some of the challenges we experienced.

Intro

In my last post we reviewed the process of converting some pieces of your Angular CLI application into a package hostable on NPM.

In this post we will discuss a real world example in which I converted one of our own Angular CLI applications into an npm package (@erdiko/ngx-user-admin) and some of the lessons we learned while doing so.

I had some challenges from local development setup, component templates and publishing my package to a namespaced npm organization.

Hopefully we can help you avoid these small pitfalls so you can focus on creating a fantastic package!

Local development & testing with npm link

To start to move code from the Angular CLI application into my new package I had to set my environment up so the Angular CLI application would reference my local code. Honestly, finding an easy way to do this became a giant pain but I think we have found an easy method. I’ll note two methods I used and the side effects of each.

npm i [directory]

The first method I used was installing the local package with each build by providing the full path to my local directory.

Here’s an example of installing the package into your destination project (assuming local package location /home/andy/ngx-user-admin and your destination package is in /home/andy/user-admin):

cd /home/andy/user-admin/app/themes/user-admin
npm i /home/andy/ngx-user-admin

One downside to this method is that you must re-install using this command after each build of your local package. Every. Time.

Clearly this is not ideal and also a productivity time suck.

npm link

The second, and preferred method, is using the npm link command to create a reference to your local code. This method ‘tricks’ npm into installing and referencing your local code instead of the npm registry code.

This method also uses a ‘live’ reference, similar to a symlink, that makes sure your destination or parent package use the most up to date code after you build.

Here’s an example installing the package into your destination project (assuming local package location /home/andy/ngx-user-admin and your destination package is in /home/andy/user-admin):

cd /home/andy/ngx-user-admin
npm link
cd /home/andy/user-admin/app/themes/user-admin
npm link @erdiko/ngx-user-admin

Please note that your linked packages will still be linked after deleting your local node_modules directories. This might cause confusion with later development and testing so be sure to delete all link references when you are done developing with this command:

npm uninstall -g ngx-user-admin

Templates and Bundling

Another difficult challenge I ran into was including an HTML template as a separate file for the components.

When creating a component in an application you can easily reference your HTML templates by a simple path and the compiler includes the files automatically. All of this kind of goes out the window when you attempt to use the AOT Compiler / RollupJS to create a UMD package. Your components now have no idea where the HTML file path is relative to.

I have seen some documentation about using a module ID to reference your module path canonically, but this seems to have been deprecated I eventually settled on reading the HTML into a variable. This method avoided the pitfalls of file paths and I could easily control the HTML I imported.

To achieve this I moved all of the component template files into files with an extension of “.tpl.ts” to make sure they were compiled, and I moved all the HTML into an exported variable to return the code.

Let’s take a closer look at the Home component template file ‘home/home.component.tpl.ts‘:

export const tpl: string= `
<div class="row">
<div class="col-xs-12">
<h1 id="welcome-title">Erdiko User Admin</h1>
</div>
</div>
...

Here is where we actually import and include this template as a variable in `home/home.component.ts`

import { tpl } from './home.component.tpl';
...
@Component({
  selector: 'app-home',
  template: tpl
})
export class HomeComponent implements OnInit {
...

I also used rollup-plugin-angular plugin to validate & minify the inline HTML I import. This allows us to check the HTML before each build as well as remove some whitespace to make this package a little smaller.

From my rollup.config.js file:

plugins: [
angular({
  preprocessors: {
    template: template => minifyHtml(template, htmlminOpts)
  }
}),

While originally VERY frustrating, I think this solution could work in a variety of packages even if you do not choose to bundle your packages up with rollup.

Publishing your package with an organizational namespace

npm’s registry is a great and free tool that allows you to publish your packages for open source software, and they even offer methods to host packages for an organization and even some to prevent public access.

After some research we found it is possible to publish your package under your organization without a paid account, despite the lack of documentation on how to do this.

When publishing your application, use the “–access public” flag.

Here is how we initially published this package:

npm publish --access public

Note: you will only need this flag on your initial publish. Subsequent updates and publishes will not require this flag.

Please note that you must add the organizational namespace to your package.json file before you attempt to publish like this:

{
  "name": "@erdiko/ngx-user-admin",
  "version": "x.x.x",
  ...
}

Documentation and Bug Tracking

Once you publish your package it’s your responsibility to provide good documentation for other developer and end-user documentation.

We plan on creating some end user documentation (coming soon!) using mkdocs since we like it’s themes and it’s ease of use. We also plan including some custom user documentation as we find it’s quite easy to generate attractive HTML from markdown files.

We will use a project called compodocs to create some information about the code itself. This project combs through your typescript code and generates documentation from docblocks. It also creates a handy report noting your comment to code coverage ratio.

It’s also VERY important to provide some channels for your end users to ask questions and report bugs. Since this is an open source project hosted on Github, our general plan is to use the Github Issue tracker to allow user’s to ask questions and report bugs. We believe this is the most straightforward method to interface with our users and to communicate with our development team.

Conclusion

Creating a package can present some significant non-coding challenges and I’m hoping we were able to save you some grief. We’re looking forward to a stable release of this package very soon, and we’re also excited about creating more packages in the future since we now have a major one under our belt.

If you have any questions about this process or about this package, feel free to reach out in the comments or on our github repo!

Converting your Angular CLI application into a NPM Module: Part One

Creating a custom application with Angular CLI is easy and fun. Converting your application into re-usable code is often the next logical step but can be quite a confusing process. We will discuss the process of converting your code into something you can actually use in multiple projects and distribute for others to use.

Intro

Angular CLI is a great tool to start an angular application easily and quickly. The latest version brings some great tools to the workflow like the module bundler WebPack.

Creating components and services quickly is a strong pro for the Angular CLI too, but at some point, you can only get so far with a single application. For example, you might find that you need to split some of the components into a discrete package for distribution.

Splitting your components and services into another package also has additional benefit of allowing you to consume and extend your code.

NPM

For those unfamiliar, NPM (Node Package Manager) is a JavaScript package registry that allows for easy distribution. It’s a great way to find packages and one stop place for information on updates and support. There are thousands of packages currently published and you should be very excited to add yours!

A package is defined by its config file, package.json which provides some of its meta data, the basic helper scripts and dependencies you need to run and compile your application. There is no doubt you have seen these files before. Angular CLI creates a basic package config when it creates your application and we will detail some of the steps to edit this file for distribution.

How to decide what to move from your app to a package

While every piece of code you write is likely a brilliant gem that should be printed on resume stock and handed out as examples of Great Code, likely only some of it is custom code deserves to be distributed to the masses.

Here are some questions you should ask yourself before deciding if a component or service is worthy of its own module:

  • Could this be a simple building block to solving a larger issue?
  • Do you see some of your angular code as being part of a solution to a larger issue?
  • Is this component something you or someone else will re-use on another project?
  • Does this solve a common problem you encounter?
  • Have you written code like this before and find yourself writing it repeatedly?
  • Have you written code like this before and find yourself writing it over and over again?
  • Are these collections of components unique enough that they are almost their own project?
  • Did you find the point of this application revolved around a few choice components?

Your module’s directory structure

The easiest way to explain how to create a new npm package is to show you a ‘complete’ package. Let’s look at an example below:

 ├── dist
 │   ├── bundles
 │   │   ├── ngx-foo-bar.umd.js
 │   │   └── ngx-foo-bar.umd.min.js
 │   ├── index.d.ts
 │   ├── index.js
 │   ├── index.js.map
 │   ├── index.metadata.json
 │   ├── node_modules
 │   ├── package.json
 │   └── src
 │       ├── foo-bar.module.d.ts
 │       ├── foo-bar.module.js
 │       ├── foo-bar.module.js.map
 │       ├── foo-bar.module.metadata.json
 │       ├── user.model.d.ts
 │       ├── user.model.js
 │       ├── user.model.js.map
 │       ├── user.model.metadata.json
 │       ├── user.service.d.ts
 │       ├── user.service.js
 │       ├── user.service.js.map
 │       └── user.service.metadata.json
 ├── index.html
 ├── index.ngsummary.json
 ├── index.ts
 ├── karma-test-shim.js
 ├── karma.conf.js
 ├── node_modules
 │   ├── @angular
 │   ├── @types
 │   ├── abbrev
 │   ├── ansi-align
 │   └── zone.js
 ├── package.json
 ├── rollup.config.js
 ├── src
 │   ├── foo-bar.module.ngfactory.ts
 │   ├── foo-bar.module.ngsummary.json
 │   ├── foo-bar.module.ts
 │   ├── user.model.ngsummary.json
 │   ├── user.model.ts
 │   ├── user.service.ngsummary.json
 │   └── user.service.ts
 ├── systemjs.config.js
 └── tsconfig.json

Two major directories to note in this example are: `src` and `dist`

The `src` or source directory is where you edit files and do your work. It also has its own `package.json` file that we will discuss more in depth later.

The `dist` or distribution directory is the compilation destination directory and is where we store the files we publish to npmjs. As I mentioned before we go more in-depth into this post.

Angular AOT Compiling

If you are reading this article you are most likely also aware of AOT (Ahead Of Time compilation).

For those of you who are familiar, this differs from “old school” angular compilation by doing a lot of the work that was normally done in the browser at the compilation time. Formerly this was all done by the JIT compiler where the browser would put together all the code. Now, this can all be handled by an offloaded backend process that “tree shakes” the code and trims off the fat.

Tree shaking this result in a much smaller codebase … translates to less data to send to the user … meaning faster load times!

This, of course, is a very brief introduction to the concept of AOT. For more information please review the official docs.

Ignoring some files in your Repo

There’s a lot of files that are created in the AOT build process you are not normally used to seeing in your previous JS/TypeScript projects. Make sure you do not include these in your repo or your npm package by adding these patterns to your .gitignore file:

*.metadata.json
*.map.js
*.js

Also, create a new type of ignore file called .npmignore.

*.ngFactory.ts
*.ts

Setting up your package config files for compilation and distribution

You should create two package.json files: one for development, and another for distribution. The development package file will allow you to edit and run your code for easy debugging. The production package file will allow you to distribute your smaller and compiled module to the masses.

Development package.json

Let’s take a look at an example development package.json file:

{
  "name": "my-package",
  "version": "1.0.0",
  "description": "An amazing module for Angular.",
  "scripts": {
    "transpile": "ngc -p tsconfig.json",
    "package": "rollup -c",
    "minify": "uglifyjs dist/bundles/my-package.umd.js --screw-ie8 --compress --mangle --comments --output dist/bundles/my-package.umd.min.js",
    "build": "npm run transpile && npm run package && npm run minify"
  },
  "types": "./index.d.ts",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/example/my-package.git"
  },
  "author": "Foo Bar",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/example/my-package/issues"
  },
  "homepage": "https://github.com/example/my-package#readme",
  "devDependencies": {
    "@angular/common": "~2.4.0",
    "@angular/compiler": "^2.4.10",
    ...
    "uglify-js": "^2.8.22",
    "webdriver-manager": "10.2.10",
    "zone.js": "^0.7.4"
  }
}

You will see the expected key values. Some are meta information about the package, etc.

There are some major differences in this file compared to the production package.json file: devDependencies & scripts.

NPM Package Scripts, as you know, are used to execute build & test commands and we need to install some devDependencies to allow you to work on your code.

Now let’s look at your production file…

Production package.json

You should create a production package.json file for your end users. This file should live in your `dist` directory as it’s really on. Here is an example production package.json file:

{
  "name": "my-package",
  "version": "1.0.0",
  "description": "An amazing module for Angular.",
  "main": "bundles/my-package.umd.js",
  "module": "index.js",
  "typings": "index.d.ts",
  "keywords": [
    "angular 2",
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/example/my-package.git"
  },
  "author": "Foo Bar",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/example/my-package/issues"
  },
  "homepage": "https://github.com/example/my-package#readme",
  "peerDependencies": {
    "@angular/core": "^2.4.0 || ^4.0.0",
    "rxjs": "^5.0.1"
  }
}

Note this file is significantly smaller than the development version. You do not need to include the devDepencies and you only point to your compiled module.

Demo-ing you Project

It’s a great idea to let end users see your package in action before they download and install it. I like to include a barebones application that implements and demonstrates my component.

This is a good way to promote your project and allows you to prove your code works.

Conclusion

We have reviewed the process of taking your code from an Angular CLI project into a fully-fledged npmjs package for others to use. We hope you can use this advice and apply it to your own projects.

We’ll explore how we ‘took apart’ one our internal Angular CLI applications and turned large portions of it into an npm package in my next post.

Thanks for reading!