Torsten Müller

Creating an npm library from existing code

published Apr 5th, 2020

Have you ever programmed something where you thought: “I need to be able to reuse this again in the future?” Me neither — that is, until sometime earlier this year when I put together a generic data table component for Angular that is generic enough to potentially be reused in other projects.

So this post is about how to extract your functionality into its own npm module and publish that module to npm so you can import and use it in future projects.

The setup

It started when I was working on the reusable Angular table component and the concepts I built into it showed that they could lead to using that table in all kinds of different projects — and even that future developments could be ported back to other projects that already are using it.

Hence the desire to abstract out component code into its own npm library, because experience has shown me that even if something is used only on one project, the introduction of a separate npm library creates another level of abstraction that prevents developers from quickly hacking in their requirements without giving thought to the greater use of the library.

Setting up the module for npm publishing

Starting out, I had already created a separate Angular module for the table display in my project, which has the following file/folder structure:

Folder structure

So the first thing to do is create the library using the Angular CLI in the following manner, from the root of the project:

 zshell 
# ng generate library @gitusername/project-name
ng generate library @diwadidu/data-table

The first line shows the generic command, whereas the second line fills that abstraction with life, using sample values, i.e. @diwadidu for the scope and data-table for the name of the library.

This command will set up a new folder structure, adding a new projects folder into the project’s root directory. Under projects, it will create a new separate space under diwadidu/data-table.

As someone once said: “There are only two hard things in Computer Science: Cache invalidation, naming things and off-by-one-errors.” The problem with naming things here is that your chosen name might already be taken. To get around that “problem”, you can scope your library as shown here, which will eventually lead to a library in npm at @username/project-name.

If you work for a company, this paradigm of scoping will also permit you to keep your libraries within the company and also in a private space and not distribute potentially very valuable information all over “the internet.” In short, I would always recommend using name spaces.

If you explore the folder created by ng generate library you’ll see that there already are a number of files in it to get you started: Under the lib directory, you’ll find stubs for a component as well as a service.

Since we’re porting our existing module into a new package, we first remove the existing stubs and move our own code into the lib folder. If you’re using an IDE such as IntelliJ to move the files, you’ll get a warning that the files you’re trying to remove are in use by other files. That dependency is mostly the public-api.ts file, to which we’re going to get in a moment.

So remove the stubs and copy your existing implementation into the src/lib under projects, for example (your folder structure might differ from mine):

 zshell 
rm -rf projects/diwadidu/data-table/src/lib/*
cd src/app
git mv your-project-dir ../../projects/diwadidu/data-table/src/lib 

So now, as promised, let’s look at the public-api.ts file in your library’s src folder. By default, it looks like this:

public-api.ts
/*
 * Public API Surface of table-lib
 */

export * from './lib/table-lib.service';
export * from './lib/table-lib.component';
export * from './lib/table-lib.module';

So it’s essentially only reexporting objects from your library, i.e. defines what will become publicly accessible. In my case, where I only want users of the library to interface with one root component and permit Typescript to reference some types I’m using internally, I change its content to:

public-api.ts
export * from './lib/components/data-table/data-table.component';
export * from './lib/data-table.module';
export * from './lib/types/data-table.type';
export * from './lib/types/page-change-event.type';

This step permits us to specify a public API to an entire collection of pieces but keep its detailed functionality, as well as any dependent components from its user. So here, we only allow the programmer using this library to use the root data-tabe.component.ts and import its module as well as use some types that are describing the data passed to and events emitted from the module. So basically, we’re describing the interface of this library component, its public api, go figure!

This permits us to internally change the make up and functionality of the package without affecting how the library is being used and knowing that as long as we don’t change the exported pieces, we won’t affect the library’s users.

Building, publishing and using the new package

One file that I haven’t mentioned yet is the README.md file, which was also generated by the Angular CLI when we originally set up our library using ng generate library .... Right now, all it contains is boilerplate which should be replaced by meaningful information on what the library does and how to use it. I’ll skip that part here.

So now, finally, we have everything set up and it’s time to build and publish our library and to include it in the project in which we originally built it. to have the library built for distribution in npm, we can again use the Angular command-line tool. In my case:

 zshell 
ng build @diwadidu/data-table --prod

This command will build the required files for publication, such as type hints files, version for the different versions of ECMAScript/JavaScript. At the end of its work, it will generate output in the shell, which ends with (paths truncated):

 zshell 
...
------------------------------------------------------------------------------
Built Angular Package!
 - from: ..../data-table/projects/diwadidu/table-lib
 - to:   ..../data-table/dist/diwadidu/table-lib
------------------------------------------------------------------------------

Now that we have built our library, we need to make is accessible. With the introduction of Angular 9 andd the Ivy compiler, there has been a change in how to publish your library which concerns the use of the --prod flag, which will use the old compiler.

To publish your library, you again use npm in the following manner:

 zshell 
npm publish --access public

This will package up your code and push it into your npm space, if you’re already logged in — if you’re not logged in, you need to do so, of course. Once the command executes, you can go and look in your npm space for your new package, which will look like this:

Screenshot of the library in your package repo

If you omit the --access public flag, npm will return with a “402 Payment Required” and ask you to log in. By default, npm assumes you’re going to store a private package, which is reserved for paying customers.

Using your new package

So now, your new package is up on npm and all that’s left to do is to update the references in your Angular project to use that new package from npm instead of the local version you have been using thus far.

In your other components, you might have imported the data table like this:

some.component.ts
import { Component, OnInit } from '@angular/core';
import { DataTableType, PageChangeEventType } from '../../../data-table/';
import { EmployeeType } from '../../types/employee.type';

export class SomeComponent {
   ...
}

That now needs to change to use the package from npm, so the changes would be:

some.component.ts
import { Component, OnInit } from '@angular/core';
import { DataTableType, PageChangeEventType } from '@diwadidu/data-table';
import { EmployeeType } from '../../types/employee.type';

export class SomeComponent {
   ...
}

The other places to check are the package.json and app.module.ts files, which now need entries for the new library:

app.module.ts
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DataTableModule } from '@diwadidu/table-lib';
import { EmployeesComponent } from './employees/components/employee-list/employees.component';

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

Discussion and Wrap-up

So after this creation of a separate npm module, what have we gained?

The most obvious benefit is reusability of functionality. If we use this mechanism to separate out common functionality, we can save time in future projects by reusing already existing npm modules in our code. This also permits to standardize the behavior and appearance of several separate apps which all are created by the same company and thus should behave the same way. This was the case for one of my clients when they set up their repositories with common functionality.

Another benefit, in particular for bigger companies, is that there is an additional barrier for developers to simply (and quickly) hacking in their own requirements — often without knowing all the use cases and therefore potentially destroying other projects with their changes in the process. The repo or pushing to npm can be restricted so that any change requests have to be reviewed and implemented by a group of maintainers, who are then responsible for making and communicating changes.

In the long run, this more cumbersome process will pay dividends when large projects cannot be roadblocked because of a bad commit by a person who didn’t fully grasp the library’s reason for being — and its intricacies.

Finally, with Microfrontends rightfully now all the rage these days, this paradigm is one possible mechanism to implement and distribute common/shared functionality between multiple separate frontends.