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.
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.
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:
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.
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.
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
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.
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…
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.
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.
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!
Tags: angular2, angularjs, javascript, npmjs, TypeScript
Categories: JavaScript, Programming, TypeScript
Lets talk!
Join our mailing list, we promise not to spam.