Building and Publishing Your TypeScript Code as a NPM Package
Tags: typescript, npm, webdev, opensource
Published: 2023-10-03 19:00:00

<a name="table-of-contents"></a>
I love open-source software. The previous couple of weeks, I posted about [how I created a key generator](https://dev.to/joaotextor/how-to-write-a-key-generator-in-typescript-3049) and [how I tested the code](https://dev.to/joaotextor/using-jest-to-bulletproof-a-typescript-class-part-1-hjo) using jest.

Today, time has come to publish that code as a library on npmjs.com, so it can be consumed by anyone.

Are you ready for this?

## Table of Contents

* [TL;DR](#tldr)
* [First, the requirements](#first-the-requirements)
* [Dependency installing](#dependency-installing)
* [Logging in to npm](#logging-in-to-npm)
* [Configuring tsconfig.json](#configuring-tsconfigjson)
* [Configure package.json](#configure-packagejson)
  * [Explaining the `npx fix-tsup-cjs`](#explaining-the-npx-fix-tsup-cjs)
* [Building our project](#building-our-project)
* [Testing](#testing)
* [Publishing](#publishing)

---

## TL;DR<a name="tldr"></a>
[(Scroll to Top)](#table-of-contents)

This article explains how to publish a TypeScript project as an open-source library on npmjs.com. It covers the following steps:

1. Ensure you have an npmjs.com account and a suitable npm version.
2. Install the `tsup` library for bundling TypeScript projects.
3. Log in to your npmjs.com account using the npm CLI.
4. Configure your tsconfig.json file with specific options.
5. Set up your package.json file with essential package details.
6. Use the `fix-tsup-cjs` library to improve CommonJS module usage.
7. Build your project with `npm run build`.
8. Test the installation with `npm install . -g`.
9. Finally, publish your package to npmjs.com with `npm publish`.

## First, the requirements<a name="first-the-requirements"></a>
[(Scroll to Top)](#table-of-contents)

First of all, before we start the publishing process, we need to make sure:

1. We've created and account on npmjs.com.
2. Our npm version is at least 9.8 (I've used 10.1, but 9.8 is fine)

## Dependency installing<a name="dependency-installing"></a>
[(Scroll to Top)](#table-of-contents)

We are going to use a library called `tsup` to bundle our TypeScript project. So, make sure you install it by running:

`npm install tsup --save`

## Logging in to npm<a name="logging-in-to-npm"></a>
[(Scroll to Top)](#table-of-contents)

After you've created your account on npmjs.com, login to it using the npm cli. You can type it using the terminal inside VS Code if you want:

`npm login`

After hitting 'Enter', you'll see the following message.

![Terminal message saying to hit Enter to open the browser](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tuvgt4zstoug7mdkd8vd.png)

Just hit 'Enter' again to open the login page in your web browser. You will receive an One-time Password, so check your email and paste it in the form.

![Web page asking for the One-time password](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxb8hsfc7l5jco8ydjmd.png)

Your CLI should show a message like this: `Logged in on https://registry.npmjs.org/.`

You are good to continue!

## Configuring tsconfig.json<a name="configuring-tsconfigjson"></a>
[(Scroll to Top)](#table-of-contents)

Here we are making some configuration so `tsup` creates the bundle correctly.

Here are the options you should enable in your tsconfig.json (some may already be enabled):

```js
{
  "compilerOptions": {
    /* Language and Environment */
    "target": "ES2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    /* Modules */
    "module": "CommonJS" /* Specify what module code is generated. */,
    "noEmit": true /* Disable emitting files from a compilation. */,
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
    /* Type Checking */
    "strict": true /* Enable all strict type-checking options. */,
    "noUncheckedIndexedAccess": true /* Add 'undefined' to a type when accessed using an index. */,
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  },
  "include": ["./"],
  "exclude": ["node_modules"] // which files to skip
}
```

## Configure package.json<a name="configure-packagejson"></a>
[(Scroll to Top)](#table-of-contents)

Configuring the `package.json` file correctly is a crucial part in packaging and publishing your code to npmjs.com, so I will briefly describe the configurations I make.

First, let's see the configuration file, and later I will explain it.

```js
{
  "name": "easy-key-generator",
  "version": "1.1.9",
  "description": "Easy library for generating secret keys.
It supports various features like groups, separators,
key format, and the possibility of generating multiples
keys at once.",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "scripts": {
    "dev": "tsc --watch",
    "build": "tsup index.ts --format cjs,esm --dts && npx fix-tsup-cjs",
    "lint": "tsc",
    "test": "jest"
  },
  "author": "João Textor",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/joaotextor/easy-key-generator"
  },
  "keywords": [
    "key generator",
    "code generator"
  ],
  "devDependencies": {
    "@types/jest": "^29.5.5",
    "@typescript-eslint/eslint-plugin": "^6.7.0",
    "@typescript-eslint/parser": "^6.7.0",
    "eslint": "^8.49.0",
    "jest": "^29.7.0",
    "prettier": "^3.0.3",
    "ts-jest": "^29.1.1",
    "tsup": "^7.2.0",
    "typescript": "^5.2.2"
  }
}
```

- **name** is the name of your package. Try choosing an unique one. Search for existing packages in npmjs.com.
- **version**: self-explanatory. I started with 1.0.0, but you're free to choose what you want. Be aware that each time you publish to npmjs.com, the next time it must contain a newer version.
- **description**: this is where you briefly describe what your packages does.
- **main**: this is where you declare the path to your main bundled file (which not yet exists). This is the javascript file which users will import using `import`.
- **module**: this is the bundled javascript file to be imported using `require`, as a commonJS module.
- **types**: the path to your `d.ts` file, which contains your types.
- **scripts**: here we created a `build` script that will call `tsup` to bundle our `index.ts`, which is our project's main file; we've passed the option --format with `cjs,esm` params, which will create the commonJS file and a ES Module file; finally, the --dts option is telling `tsup` to create our type file with `d.ts` extension. Next we will call `npx fix-tsup-cjs`. Why? I'll explain later on.
- **author**: This is where you put your name, so everyone can blame you publicly if your library introduce bugs into their projects (just kidding...or not!)
- **license**: You can inform an open-source license here if you want.
- **repository**: want to make easier for people to yell at you online? Place the repository to your library here :sweat_smile:. They might also submit a PR to fix your undesired bugs :smile:.

### Explaining the `npx fix-tsup-cjs`<a name="explaining-the-npx-fix-tsup-cjs"></a>
[(Scroll to Top)](#table-of-contents)

When bundling your project using `tsup`, you might notice that when importing the CommonJs module using something like `const KeyGenerator = require("easy-key-generator");` when calling your class' constructor you must type this: `new KeyGenerator.default()`. Also, when calling a method, let's say `generate()`, you need to call it by using `KeyGenerator.default().generate`.

This sucks, right? Why can't you instantiate your class by using only `KeyGenerator()`?

Well, the library [`fix-tsup-cjs`](https://github.com/u3u/fix-tsup-cjs) enables you to do exactly that, that's why we executed it after bundling our project using `tsup`.

## Building our project<a name="building-our-project"></a>
[(Scroll to Top)](#table-of-contents)

Well, now that everything is setup correctly, is time for us to build our project.

Just type `npm run build` in your terminal in the root of your project and hit 'Enter'.

## Testing<a name="testing"></a>
[(Scroll to Top)](#table-of-contents)

Before publishing the package, your should test installing it.

To test the install, type `npm install . -g` in the root of your project. NPM should be able to install it globally as any other package.

If anything goes wrong in this step, it means something is wrong with your configuration.

## Publishing<a name="publishing"></a>
[(Scroll to Top)](#table-of-contents)

Finally, it's time to publish your package to npmjs.com. Type `npm publish`, get your fingers crossed and TA-DA!

Congratulations, you've successfully published your TypeScript code to npmjs.com! It is now available to the npm community. 

In this guide, we've covered the essential steps to publish your TypeScript project on npmjs.com. By following these steps and maintaining your package, you can share your code with the world and contribute to the open-source community. Happy coding!

If you did read through the entire post until here, please leave a reaction and a comment, and hit the follow button because **next week we will be learning how to automatize the work of publishing a node package by using GitHub Actions!**

Until next week!