Build an Angular Library With NgPackagr Build your own Angular library and publish it to NPM 1033 words. By Jeff Delaney Created May 2, 2018 Last Updated May 2, 2018 Code Slack #pro #angular In this lesson, I will show you how to build an Angular Library from scratch using NgPackagr. My main motivation for this article is that Google Analytics recently changed their main API to gtag.js, but solid Angular libraries were lacking, so I decided to build one from scratch. NgPackagr made the bundling process very easy. Angular packages are standardized by the Angular Package Format, but generating this output manually is difficult. NgPackager will do all the stuff you don’t want to deal with, like generating the bundles (FESM2015, FESM5, and UMD), creating your types, and ensuring that the end result is AOT compatible. Full source code [Angular NgPackager Tutorial](https://github.com/AngularFirebase/103-angular-ngpackagr-tutorial). IMPORTANT Update for Angular v6.0 Angular CLI v6.0 introduced a new command that will save much of the manual configuration required in the this lesson. # Generate a library ng generate library awesome # Build it np run awesome:build # Install it in your app npm install dist/awesome While the code in the following steps is still valid, I highly recommend using the CLI to eliminate the manual creation of files. Step 1 - Build an Angular CLI Project Let’s generate an app and give it a module and component. The app itself is not part of the library, but will be used for integration testing. ng new awesomePackage --routing cd awesomePackage Now lets create a feature module and add some resources to it. ng g module toast ng g service toast/toast -m toast ng g component toast/toast-message --export I’m not going to cover how to build the actual app, but it’s just simple toast notification system. It includes a service that controls he message content/visibility and component that shows the actual message to the end user. Step 2 - Setup your Lib Create a folder in the root of your project named lib (or whatever you want) - this is where the source code for our library will exist. Inside this the lib folder create the following elements. src/ source code package.json package definition public_api.ts to export your public Angular modules README.md so people know how to use your library Then move the files from your toast feature module to the src folder in lib. Creating an Angular Module for Root The library’s module works mostly like it does in Angular, but the main difference is the way services are provided. In many cases, developers may need to pass configuration options to a library. This can be accomplished by defining a forRoot method, which returns an object typed to ModuleWithProviders interface. import { NgModule, ModuleWithProviders } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ToastService } from './toast.service'; import { ToastMessageComponent } from './toast-message/toast-message.component'; @NgModule({ imports: [ CommonModule ], declarations: [ToastMessageComponent], exports: [ToastMessageComponent] }) export class ToastModule { public static forRoot(config): ModuleWithProviders { return { ngModule: ToastModule, providers: [ ToastService, { provide: 'config', useValue: config } ] }; } } The end-user of the lib would use this module in their app like so: ToastModule.forRoot({ apiKey: 'shhhhh' }) Injecting Configuration Options If we need to use the data passed to the module via forRoot, we can do so by using the @Inject decorator in the constructor. @Injectable() export class ToastService { constructor( @Inject('config') private config ) { } } Step 3 - Configure for NgPackagr NgPackagr makes a few reasonable requirements of us to perform its magic. Install Install NgPackagr. npm install ng-packagr --save-dev Public API In lib/public_api.ts export the interfaces that the developer should be able to access. export * from './src/toast-message/toast-message.component'; export * from './src/toast.module'; export * from './src/toast.service'; lib/package.json In the lib/package.json we will define our library’s NPM details. Make sure the name of the package is available on NPM if your project is open source. The special section is ngPackage, which is used by NgPackagr to locate the public API file for your project. Your package should not have any dependentcies, only peerDependencies and devDependencies { "name": "super-toasty", "version": "0.0.1", "author": "Jeff Delaney <hello@angularfirebase.com>", "license": "MIT", "peerDependencies": { "@angular/core": ">=5.0.0", "@angular/common": ">=5.0.0" }, "ngPackage": { "$schema": "./node_modules/ng-packagr/ng-package.schema.json", "lib": { "entryFile": "public_api.ts" }, "dest": "dist" } } NPM Script In the package.json (in the root of the project, not lib) add a build script. "scripts": { // ... "build:lib": "ng-packagr -p lib/package.json" } Now run npm run build:lib and you should see output like this: Step 4 - Do some Integration Testing Now it’s time to do some integration testing. Install the local package and serve the Angular app. npm i lib/dist ng serve Then import the library in the app module. import { ToastModule } from 'super-toasty'; @NgModule({ imports: [ ToastModule.forRoot({ brand: 'fireship.io' }) ] }) Because we provided a service, we can inject it into our components. import { ToastService } from 'super-toasty'; @Component({...}) export class AppComponent { constructor(toast: ToastService) { toast.show() } } And because we exported a component, we can use it in our templates. <toast-message></toast-message> Step 5 - Publish to NPM Publishing to NPM is super easy. First, make sure you’re logged into your account, then run the publish command pointing to lib/dist. npm whoami npm publish lib/dist Congrats! The world is free to use for Angular Package by running: npm install your-awesome-package Best Practices when Writing Angular Libraries On a final note, let’s talk about some best practices when building angular libraries. Don’t touch the DOM If your library touches he DOM directly, such as document.getElementById('foo'), it will break other developer’s apps when they use Angular with server or web worker platforms. If you must do this, wrap your code in a try/catch block. Strong Type Your public API should be strong-typed. Developers like Angular because of the awesome tooling provided by TypeScript. Try to make end-users of your package as happy as possible by leveraging types. Bad someMethod(options: any) { ... } Good export interface Options { foo: string; bar: boolean; } someMethod(options: Options) { ... } Write Documentation Duh! Write Unit and Integration Tests As your library grows in complexity it becomes easy to introduce bugs and regressions. Write some tests an implement use continuous integration to ensure every commit meets your spec. Tests will also make it easier for other developers to contribute to the codebase without breaking everything. In this lesson, I will show you how to build an Angular Library from scratch using NgPackagr. My main motivation for this article is that Google Analytics recently changed their main API to gtag.js, but solid Angular libraries were lacking, so I decided to build one from scratch. NgPackagr made the bundling process very easy. Angular packages are standardized by the Angular Package Format, but generating this output manually is difficult. NgPackager will do all the stuff you don’t want to deal with, like generating the bundles (FESM2015, FESM5, and UMD), creating your types, and ensuring that the end result is AOT compatible. Full source code [Angular NgPackager Tutorial](https://github.com/AngularFirebase/103-angular-ngpackagr-tutorial). IMPORTANT Update for Angular v6.0 Angular … Upgrade to PRO for the Full Lesson Monthly $25/month Unlimited access to PRO courses Cancel anytime Slack #pro-member invite AngularFirebase Survival Guide Book No ads Quarterly $50/3mo All monthly tier benefits 💰 33.33% discount 🔥 Free Sticker Lifetime $300/one-time All monthly tier Benefits 💸 One-time payment 👕 Free Commemorative T-Shirt Login with Google ---- OR ---- You must be logged in to view this content. Login