Greetings, inspector! If you wanna see more, check my Github: mococa 😏

author avatarLuiz Felipe Moureau

19/07/2023/Articles

How to create a Node.JS library in TypeScript

In this article, I cover how to create a public library and publish it on NPM from scratch. And it's easier than you imagine.
How to create a Node.JS library in TypeScript

NPM Libraries

Many times, we find ourselves in the following exhausting situation: Copying code from project to project endlessly. Sometimes we even think about creating a library for it, but then we hesitate whether it’s worth spending our time on it. And most of the time, it is worth it!

Having your library in the infamous node_modules is not difficult. Just take a look:

Requirements

  1. Node and NPM installed
  2. NPM account and token
  3. Github account
  4. Goodwill

That’s it.

Step by Step

  1. Create an empty directory for your library. After all, it’s just a Node project.
  2. Navigate to the directory and run yarn init -y.
  3. In the generated package.json, you’ll need to make a few changes:
    • The name field is the name of your library and also how people will install it. So you have two options: be creative with the name or prefix it with your Github username, like @mococa/toastr, for example. You can check if the name is available by running yarn add LIBRARY NAME by going to the NPM website and searching for the desired name.
    • The version field is important. It is recommended to start with version 0.0.1. When you want to make changes, you’ll need to update it.
    • You’ll need a few more fields that you might not be familiar with:
      • homepage where you will put the link to your library’s Github repository or Github Pages.
      • repository.url where you will put the link to your library’s Github repository ending with .git.
      • module, types, main will be the entry points of your library.
      • peerDependencies where you will list the dependencies that you expect the users to have.
      • devDependencies dependencies to assist you.
      • dependencies dependencies to make your library work.
  4. You may need to install a few libraries to be able to build your TypeScript project.

Without further ado, here’s an example of how it would look in your package.json:

{
  "name": "my-awesome-library",
  "version": "0.0.1",
  "author": "YOUR LOVELY NAME",
  "license": "MIT",
  "homepage": "https://github.com/YOUR-GITHUB/REPO",
  "repository": {
    "url": "https://github.com/YOUR-GITHUB/REPO.git"
  },
  "main": "dist",
  "module": "dist",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "npx tsc --build tsconfig.json",
    "watch": "tsc -w",
    "prepublishOnly": "npm run build"
  },
  "devDependencies": {
    "@types/node": "^16.14.0",
    "ts-node": "^10.9.1",
    "typescript": "^5.1.6"
  },
  "keywords": [
    "my",
    "first",
    "lib",
    "typescript"
  ]
}

Alright. After doing that, notice that in the build script, we are configuring it based on the tsconfig.json, so let’s create it:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["es2020", "dom"],
    "declaration": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": false,
    "inlineSourceMap": true,
    "inlineSources": true,
    "experimentalDecorators": true,
    "strictPropertyInitialization": false,
    "esModuleInterop": true,
    "typeRoots": ["./node_modules/@types"],
    "noEmit": false,
    "outDir": "./dist"
  },
  "exclude": ["node_modules", "dist"]
}

Now let’s create a .gitignore and a .npmignore file. The .npmignore file is used to prevent unnecessary files from being sent to the user’s node_modules folder.

.npmignore:

*.ts
!*.d.ts
.github

Perfect.

Now, you’ll place your files in a folder (the convention is to name this folder lib), such as /lib/file-that-does-something.ts, and in that file, you put the logic. It’s important that any used types are declared if you want to export them as well (it’s always good to have the types of the libraries available).

Then, create a /lib/index.ts file where you import and export everything as needed. What you export from this file will be usable by anyone who installs your library.

Example:

// lib/greet.ts
export interface GreetProps {
  name: string;
}

export const greet = ({ name }: GreetProps) => {
  console.log(`Hello, ${name} ! This is my library working`)
}
// lib/index.ts
import { greet, GreetProps } from './greet';

export { greet, GreetProps };

Testing

To test your library without having to publish it to NPM, you can use npm link!

Now you can run yarn build and then yarn link.

After that, go to a different project to test and run yarn link my-awesome-library, and in the blink of an eye, you have your library in the project to test. Perform the tests, and when you’re done, run yarn unlink my-awesome-library, and then yarn unlink in the library.

Publishing to NPM

Assuming you already have your NPM account and generated token (if you don’t have it and read this far, you deserve 7 gentle slaps), take the token and add it to the repository’s secrets: https://github.com/YOUR-GITHUB/REPOSITORY/settings/secrets/actions

Add something like NPM_TOKEN.

Got it? Great. Let’s continue with the code.

Create a file .github/workflows/NPM.yml.

In that file, add the following:

name: Publish to NPM

on:
  release:
    types: [created]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: "16.14.0"
          registry-url: "https://registry.npmjs.org"

      - name: Install Dependencies
        run: yarn install

      - name: Build
        run: yarn run build

      - name: Publish package on NPM
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

What this is doing is: Every time there is a new Github release, we trigger an action to update the NPM library.

And that’s it! When everything goes well, you can use your public library.

Luiz Felipe Moureau

Fullstack developer at Space.game