Building reusable Angular NPM packages

Computas AS
Compendium
Published in
4 min readMar 13, 2018

by Magnus Stuhr — Principal Engineer

My previous blog post argued that one of the most important principles of programming is to avoid code-duplication, and rather reuse existing code for the same functionality — it aimed at providing insights into how anyone could implement their own code-sharing strategy. This blog post will further elaborate on this by showing a concrete example of how you in no time can develop and publish your own Angular NPM packages that can be reused across your different applications.

The first thing we need to do when publishing our own NPM packages, is to make sure we have an NPM registry to push our packages to. If you want to push your package to a public repository, you do not need to set up any authentication. However, if you have a private NPM registry of some sort, you need to follow the authentication setup instructions for that repository, as we will not go into details about that in this post. Either way, you should end up with a .npmrc file in the root folder of your project, containing a list of the registries your application should retrieve packages from:

.npmrc

@<your_registry_name>:registry=<your_registry_url>
always-auth=true

Building and publishing NPM packages

This section includes a step-by-step guide on how to build and publish your own NPM packages.

First of all, when developing NPM packages we usually want to include peer-dependencies and dev-dependencies instead of the regular direct dependencies. Peer-dependencies are the packages required for including our self-made NPM package, and dev-dependencies are the dependencies used for developing our NPM package. We should include regular dependencies for libraries that are only required in our NPM package, and that do not need to be referred to outside the package-scope. See the package.json dependencies for the full documentation.

1 All components and services should have their own “index.ts” file

When coding NPM packages, make sure to export your code correctly, in order for it to be used properly from the outside. All of our components and services should have their own index.ts file that export the functionality.

<component/service>/index.ts

export * from '<your_component/service_name.component/service/module>';

Example:

export * from './dialog-provider.module';
export * from './dialog-provider.service';

2 Your app module should have an “index.ts” file

Finally we need to create an entrypoint where the functionality of all our modules are included:

index.ts

export * from '<your_component/service_paths>'; (no file extension declaration after path)

Example:

export * from './dialog-provider';

3 Add Gulp-integration for proper building

When building our package, we need to inline the HTML and CSS into Angular component decorators.

3.1 Install the Gulp plugin

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

3.2 Add a new Gulp task

Make sure to include your own relative paths instead of the paths below, if your project structure is different.

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

4 Add build steps in the “scripts” object in your package.json

"scripts": {
...
"inline-build-templates": "gulp inline-build-templates",
"ngc-build": "ngc -p ./tsconfig.lib.json
...
}

5 Install typings

We need to install the typings necessary, in order for the package to build without errors.

npm install --save-dev @types/core-js

5.1 Make sure you have a “tsconfig.lib.json” file in your root folder

tsconfig.lib.json

{
"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
}
}

5.2 Add TypeScript typings and entrypoint for our package

package.json

...
"main": "./lib/index.js",
"typings": "./lib/index.d.ts"
...

6 Add the “lib” folder to the “files” array in your package.json

The “lib” folder is the raw output of our built package.

"files": [
...
"lib"
...
]

7 Add rimraf integration

Add rimraf-integration in order to cleardown existing files for each build.

7.1 Install rimraf

We want to make sure no old files linger for our newer builds.

npm install rimraf --save-dev

7.2 Add the rimraf build command to the “scripts” object in your package.json file

"scripts": {
...
"cleardown": "rimraf build lib",
"package": "npm run cleardown && npm run inline-build-templates && npm run ngc-build"
...
}

8 Build your package

npm run package

9 Push your package to the NPM registry

For your final release (i.e. v1.0.0):

npm publish

For beta-releases (i.e. v1.0.0-beta1):

npm publish --tag beta

Congratulations, your package should now have been pushed to the NPM registry!

Magnus Stuhr

Principal engineer / head of the Data Science community in Computas, and organizer for the Semantic Technologies group on meetup.com. Above all loves to code.

--

--

Computas AS
Compendium

IT solutions provider based in Norway. Knowledge sharing is important in our culture and we work closely with customers to make the best solutions.