Publishing a npm library from an Angular-cli project posted on Aug 18, 2017

Angular-cli is so good it makes working in Angular without it feel painful, but it focuses is on creating apps rather than libraries which can be re-used across projects or published with NPM. This post covers the steps to take a module and do just that.

First get the repo

It will all make a lot more sense with a copy of the code to look at so, assuming you already have angular-cli installed, clone the repo.

  

git clone https://github.com/JayChase/ngx-cli-library.git
cd ngx-cli-library
npm install
ng serve

The app itself is pretty basic, there is a single module called TestLibraryModule which exports a test component. This is the module we want to use as a library in other projects.

Isolate the library code and move templates and css inline

We don’t want to affect the angular-cli app in any way or accidentally include anything but the TestLibraryModule in our library. The easiest way to do this is to take the whole module and move it to a new build folder in the project root (outside of the src folder). At the same time there is another problem to solve as well. Angular cli uses external files for CSS and HTML, which is great because to makes working your working environment a lot easier, however this causes problems in external libraries. The best way round this is to move everything into the component when we copy it. To do this we will need gulp and the package gulp-inline-ng2-template.

  

npm install gulp gulp-inline-ng2-template --save-dev

Also add a new file gulpfile.js into the project root and add the following gulp task.

  

const gulp = require('gulp');
const inlineNg2Template = require('gulp-inline-ng2-template');

gulp.task('inline-build-templates', () => {
    return gulp.src(['./src/app/test-library/**/*.ts', '!./src/app/test-library/**/**.spec.ts'])
        .pipe(inlineNg2Template({
            target: 'es5',
            useRelativePaths: true
        }))
        .pipe(gulp.dest('./build'));

});

This will get all the ts files under ./src/app/test-library that are not tests, inline the HTML and CSS into the component and then output the results to ./build. Add a script to package.json to run the task. This can then be used in the final build.

  

"inline-build-templates": "gulp inline-build-templates"

Compile the content of the build folder

Now the build folder is ready add a new tsconfig file ./tsconfig.lib.json. This one will be used to build the dist folder and output the js to another new folder ./lib.

  

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": true,
        "declaration": true,
        "outDir": "./lib",
        "stripInternal": true,
        "lib": [
            "es6",
            "dom"
        ]
    },
    "include": [
        "./build/**/*"
    ],
    "files": [
        "./node_modules/@types/core-js/index.d.ts"
    ],
    "angularCompilerOptions": {
        "skipTemplateCodegen": true
    }
}

Also add an npm script to package.json to run the build.

  

"ngc-build": "ngc -p ./tsconfig.lib.json",

The build is now working. You can test it by running:

  

npm run ngc-build

Change package.json to point only to the library

Package.json is essentially the meta data for the library so make sure the following fields are set correctly or the library will not work properly when installed and imported into another project.

  

{
 ...
  "files": [
    "lib"
  ],
  "main": "./lib/test-library.module.js",
  "typings": "./lib/test-library.module.d.ts",
  ...
}

Finish the build

There is one more build task to add. This one will cleardown the build folders to make sure no orphaned files get stuck in the build.

Install rimraf.

  

npm install rimraf --save-dev

Add a package.json script for it and another to run all of the build steps in order. The prepare script will always be ran before publishing the package to NPM.

  

    "cleardown": "rimraf build lib",
    "prepare": "npm run cleardown && npm run inline-build-templates && npm run ngc-build"

Publish the library

Now we are ready to publish the package. First check these fields are all correct in package.json.

  

{
  "name": "ngx-cli-library",
  "version": "1.0.3",
  "license": "MIT",  
  "author": "JayChase",
  "keywords": [
    "Angular",
    "Angular-cli",    
    "NPM",
    "library-demo"
  ],
  "private": false,
  ...
}

Check the readme.md. This is used as the content for the library page on npmjs.com. Finally login and publish the package. Note you won’t be actually ably to publish with the name ngx-cli-library as it already exists.

  

npm login
npm publish

Test it out

If all has gone well you can now use the package in another project. Create a new project and install the library.

  

ng new library-test
cd library-test
npm install ngx-cli-library --save

Import the module in app.module.ts

  

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { TestLibraryModule } from 'ngx-cli-library';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    TestLibraryModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

and use the TestComponent in app.component.html.

  

<app-test></app-test>

All done

That’s about it. You now have a reusable Angular module.