DEV Community

Cover image for Create and Publish Your First NPM Package: A Comprehensive Guide
Martin Persson
Martin Persson

Posted on • Edited on

Create and Publish Your First NPM Package: A Comprehensive Guide

Table of Contents

Introduction

Node Package Manager (NPM) is the world's largest software registry, housing over a million packages that enable developers to share and borrow packages, and assemble them in powerful new ways. An NPM package is a reusable piece of code that you can download and install into your projects, encapsulating functionality that can be easily shared across different projects or even with the community at large.

You can find the complete code and examples for this tutorial in the GitHub repository.

Why Create an NPM Package?

Creating an NPM package allows you to:

  • Modularize Code: Break down complex applications into manageable, reusable components.
  • Share Your Work: If you've created a function or component that others might find useful, packaging it allows you to share it easily with others.
  • Contribute to the Community: Many developers contribute packages to NPM that the entire community can benefit from. By publishing a package, you too can contribute to this ecosystem.
  • Control Versions: Easily manage different versions of the code across different environments.

What Will This Tutorial Cover?

In this tutorial, we'll guide you through the process of creating, packaging, and publishing a basic NPM package using TypeScript. Along the way, we'll demonstrate best practices in modular development and introduce you to tools such as Jest for testing and Rollup for bundling. Whether you're looking to share your own projects with the world, learn about TypeScript's powerful type system, or just organize your code more effectively, this guide will get you started on the right track.

The key components we'll explore include:

  • TypeScript: How to leverage TypeScript for adding strong typing to your package, enhancing code quality and maintainability.
  • Jest: We'll incorporate Jest for unit testing, ensuring the reliability of your package.
  • Rollup: We'll use Rollup to bundle your TypeScript files, making them ready for distribution.
  • Packaging and Publishing: The essential steps to prepare your package for distribution, including setting up the package.json file and publishing to the NPM registry.

By the end of this tutorial, you'll have a clear understanding of how to build a robust, reusable package with TypeScript and have the knowledge to contribute your own packages to the broader development community.

Prerequisites

Before we begin, please ensure you meet the following prerequisites:

  • Node.js and NPM installed: Make sure you have both Node.js and NPM installed on your computer. You can download them from the official website.
  • Basic Understanding of TypeScript: This tutorial assumes that you have some familiarity TypeScript. You don't need to be an expert, but you should know the basics.
  • An Account on NPM: If you plan to publish your package to the NPM registry, you'll need an account. You can sign up here if you don't have one.

Getting Started

Setting Up Your Development Environment

  • Creating a New Project Folder: Create a new folder and open up your editor.
  • Initializing a New Project: The first thing we need to do is run npm init in the terminal and follow the prompts.

You should now have a package.json file, and it should look something like this:

{
  "name": "my-package",
  "version": "1.0.0",
  "description": "my npm package",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Martin Persson",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Installing Dependencies: To get started with our project, we'll need to install several dependencies that will help us in development, testing, and bundling our NPM package. Here's a breakdown of the dependencies and what they're used for:

  • TypeScript: The core compiler for TypeScript.
  • Jest: A JavaScript testing framework.
  • Rollup: A module bundler for JavaScript.
  • ts-jest: A preprocessor with source map support to help use TypeScript with Jest.
  • @types/jest: The TypeScript definitions for Jest.
  • rollup-plugin-typescript2: A plugin to use TypeScript with Rollup.
  • nodemon: A utility that will monitor for any changes in your source and automatically restart your server.
  • concurrently: We'll be using concurrently to run multiple npm scripts simultaneously during development.

To install the dependencies, run the following commands:

Typescript
npm install typescript

Jest
npm install jest @types/jest ts-jest

Rollup
npm install rollup rollup-plugin-typescript2

Nodemon
npm install nodemon

Concurrently
npm install concurrently

In your package.json, you should see all the dependencies that we installed (note: you might have different versions than the ones listed below):

"dependencies": {
    "@types/jest": "^29.5.3",
    "concurrently": "^8.2.0",
    "jest": "^29.6.2",
    "nodemon": "^3.0.1",
    "rollup": "^3.28.0",
    "rollup-plugin-typescript2": "^0.35.0",
    "ts-jest": "^29.1.1",
    "typescript": "^5.1.6"
  }
Enter fullscreen mode Exit fullscreen mode

Configuration

Rollup

Rollup is a powerful module bundler that we'll use to bundle our TypeScript files, making them ready for distribution. We'll need to create a configuration file for Rollup to understand how to bundle our package.

Create a new file in your project root called rollup.config.js, and add the following code:

const typescript = require("rollup-plugin-typescript2")
const pkg = require("./package.json")

module.exports = {
  input: 'src/index.ts',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      exports: 'named',
      sourcemap: true,
      strict: false
    }
  ],
  plugins: [
    typescript()
  ],
}
Enter fullscreen mode Exit fullscreen mode

Typescript

To set up TypeScript for our project, we'll create a tsconfig.json file at the root of our project directory. This file guides TypeScript on how to compile our code and includes the following configurations:

{
  "compilerOptions": {
    "outDir": "dist",
    "module": "esnext",
    "target": "es5",
    "lib": ["es6", "dom", "es2016", "es2017"],
    "sourceMap": true,
    "allowJs": false,
    "declaration": true,
    "moduleResolution": "node",
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "rollup.config.js"]
}
Enter fullscreen mode Exit fullscreen mode

Writing Your First TypeScript File

Now that we have TypeScript and Rollup configured, we'll need to update our package.json file to include the necessary scripts and main entry point.

main: This points to the compiled entry file of our package. We'll set it to dist/index.js, the location where Rollup will compile our TypeScript code.

scripts: Here, we'll define various commands that help in the development, building, and watching of our project:

  • build: This command will run Rollup with the configuration file, compiling our TypeScript code into the dist folder.
  • dev: This command uses concurrently to watch TypeScript files and restart the server with nodemon whenever changes are detected.

Here's how the updated package.json file should look:

{
  "name": "my-package",
  "version": "1.0.0",
  "description": "my npm package",
  "main": "dist/index.js",
  "scripts": {
    "build": "rollup -c",
    "dev": "concurrently \"tsc --watch\" \"nodemon ./dist/index.js\""
  },
  "author": "Martin Persson",
  "license": "ISC",
  "dependencies": {
    "@types/jest": "^29.5.3",
    "concurrently": "^8.2.0",
    "jest": "^29.6.2",
    "nodemon": "^3.0.1",
    "rollup": "^3.28.0",
    "rollup-plugin-typescript2": "^0.35.0",
    "ts-jest": "^29.1.1",
    "typescript": "^5.1.6"
  }
}
Enter fullscreen mode Exit fullscreen mode

Creating the Source Directory and Files

In this section, we will set up our main source folder and create a simple "Hello World" program. Follow these steps:

  • Create the src Directory: This will be the main directory for all our TypeScript source files.
  • Create the index.ts File: Inside the src directory, create a file named index.ts.
  • Edit the index.ts File: Open the index.ts file and add the following code:
console.log("Hello world")
Enter fullscreen mode Exit fullscreen mode

Run Your Code: Now you're ready to run your code! In your terminal, execute the following command:

npm run dev
Enter fullscreen mode Exit fullscreen mode

You should see the "Hello world" message printed to your terminal. Additionally, thanks to Nodemon, any changes you make to the index.ts file will be automatically detected and rerun, allowing you to see your updates in real time.

Adding Jest for Testing

Testing your code is a fundamental practice in software development that can help you catch errors early and make sure that your code functions as expected. With Jest, a popular JavaScript testing framework, you can write and run tests in a structured and efficient way.

In this section, we'll walk through setting up Jest for a TypeScript project, writing a test for a simple function, and running the test.

Configuring Jest for TypeScript

Since Jest is originally designed for JavaScript, we need to configure it to work with TypeScript files. Follow the steps below:

Create a new file named jest.config.js in your root directory and add the following configuration:

module.exports = {
  preset: 'ts-jest',
  testMatch: ["**/__tests__/**/*.test.ts"],
};
Enter fullscreen mode Exit fullscreen mode
  • preset: 'ts-jest': This line tells Jest to use the ts-jest preset, which contains the settings necessary to transpile TypeScript code.
  • testMatch: This is an array of glob patterns that Jest uses to detect test files. The given pattern "/tests//*.test.ts" will match any .ts files inside a tests directory that end with .test.ts.

This basic configuration is enough to get started with testing TypeScript files, but Jest can be customized further depending on your specific needs.

Writing a Test

Now, let's write a test for our sayHello function. The function code looks like this and you can add it to your index.ts file:

export const sayHello = (name: string) => `Hello, ${name}`
Enter fullscreen mode Exit fullscreen mode

Create a test folder in your directory called tests and inside create a file named index.test.ts.

Before we can run our test, we need to add:

"test": "jest",
Enter fullscreen mode Exit fullscreen mode

To our "scripts" section inside package.json.

To run the test, type npm run test in the terminal.

If everything is set up correctly, Jest will find and run the test file, and you should see a success message in your terminal.

Publishing to NPM

After setting up and testing your TypeScript project, it's time to publish it to the npm registry. Here's how you can do it:

Add publish scripts

Before we can publish our package we need to make updates to our scripts inside package.json. Add the following:

   "prepare": "npm run build",
   "prepublishOnly": "npm test"
Enter fullscreen mode Exit fullscreen mode

Logging into NPM

Before publishing your package, you need to log into npm, which requires a user account. If you don't have an account, you can sign up here.

Open your terminal and enter the following command:

npm login
Enter fullscreen mode Exit fullscreen mode

You'll be prompted to enter your username, password, and email address associated with your npm account.

Publishing the Package

Before publishing your package, ensure that you have chosen a unique name for it. The name must be one that hasn't been used before on npm. Update the "name" field in your package.json file to your chosen name.

Example:

"name": "your-unique-package-name",
Enter fullscreen mode Exit fullscreen mode

With everything set up and your code tested, you're ready to publish your package.

Verify Your package.json File: Ensure that your package.json file includes all necessary details, such as name, version, description, main entry file, etc.

Publish the Package: Run the following command:

npm publish
Enter fullscreen mode Exit fullscreen mode

You could also automatically increment the version while publishing. (thanks to @tqbit for pointing that out)

npm version patch --no-git-tag-version && npm publish --access public
# Instead of patch, you can use 'minor' or 'major'
Enter fullscreen mode Exit fullscreen mode

After successfully publishing your package, you can verify its availability by visiting your npm profile. Log in to npm's website and navigate to the "packages" section under your profile. Here, you should see your newly published package listed among any others you may have published previously.

If everything looks good, congratulations! You've successfully published a TypeScript package to npm. Now others can install and use your package by running:

npm install your-unique-package-name
Enter fullscreen mode Exit fullscreen mode

Remember to maintain and update your package as needed, responding to issues and continuing to enhance its functionality. Happy publishing!

Note: If you encounter a 403 Forbidden error during the publishing process, it may be related to the package name already being used. In this case, choose a different, unique name and try again.

Conclusion

Creating, testing, and publishing a TypeScript package to the NPM registry is a fulfilling process that adds value to the broader development community. In this tutorial, we've walked through every step of this journey—from setting up your development environment, understanding dependencies like TypeScript, Jest, and Rollup, to writing your first piece of code and creating unit tests. Finally, we learned how to prepare and publish the package, making it available for others to use.

The principles and practices detailed in this guide not only enable you to contribute your own packages to the NPM ecosystem but also provide a solid foundation for modular development. By understanding how to break down code into reusable components, you can write cleaner, more maintainable code, whether you're working on personal projects or contributing to larger team efforts.

As you continue to explore and grow as a developer, remember that the community is an ever-evolving space filled with opportunities for learning and collaboration. Whether it's by enhancing your existing packages, creating new ones, or contributing to others, you're now equipped with the skills and knowledge to engage actively with the development world.

Happy coding, and don't hesitate to share your creativity and expertise with the community!

Top comments (8)

Collapse
 
tqbit profile image
tq-bit

Very helpful guide (especially because it implements a test library)

What I would like to add is that, together with npm publish, you can increment the package.json's version before a release, e.g. like so:

npm version patch --no-git-tag-version && npm publish --access public
# Instead of patch, you can use 'minor' or 'major'
Enter fullscreen mode Exit fullscreen mode

This helps to keep you semver in sync with your releases.

Collapse
 
martinpersson profile image
Martin Persson

Thank you!

That's a great point, I will add it to the tutorial. Much appreciated!

Collapse
 
robinamirbahar profile image
Robina

Good Work

Collapse
 
joxx profile image
joachim kliemann

Thank you! Very helpful tutorial.

Collapse
 
martinpersson profile image
Martin Persson

Glad you liked it!

Collapse
 
cunkouzhuo profile image
cunkouzhuo

Nice article!

Collapse
 
martinpersson profile image
Martin Persson

Thank you! Glad you liked it

Collapse
 
demianeen profile image
Feliche-Demian Netliukh

Hi everybody! Did only I get the error that I can't use ES modules without type module after adding sayHello to index.ts?

It seems like rollup is compiling to cjs, but tsc not. As far as I understand this is because rollup needs ES modules for treeshaking, right? What to do then?