How to create and publish a npm package

Denis Homolík
5 min readMar 2, 2020

--

Creating an npm package can be surprisingly confusing and official resources only state the bare minimum required for publishing a package. This article is a step by step guide on how to create a package using technologies ESlint, Prettier, Rollup, Babel, and Jest. It may sound complicated, but they are great tools to help you write code others can use.

This is what I’ve learned while working on my package @xcorejs/ui. I’m going to use yarn in this article, but it’s not required.

Package setup

First thing you should do is create a new directory and setup an npm package with:

yarn init

Then create a src folder where you will place your code. Also, it’s a really good idea to set up a git repository.

git init

Add .gitignore file:

node_modules
yarn.lock
# package builds
lib
dist

Optionally you can add info about the package to your package.json, so it could look like this:

{
"name": "@alfonz/random", // your package can be scoped to your account name
"private": false,
"version": "0.0.1", // use semantic versioning

"author": "Denis Homolik",
"license": "MIT",
"description": "Simple package of random related utilities"
}

Lint code with Prettier and ESlint

Before you start coding, you should set up a linter and a code formatter. At the time of writing this article, the most widely used linter is ESlint, and the most used code formatter is Prettier.

Install ESlint and Prettier like this:

yarn add -D eslint eslint-config-prettier eslint-plugin-prettier prettier

Then create a eslint.config.js file to configure ESlint:

{
"plugins": ["prettier"],
"extends": [
"eslint:recommended",
"prettier"
],
"parserOptions": {
"sourceType": "module"
},
"env": {
"browser": true,
"jest": true,
"es6": true,
"node": true
},
"rules": {
"prettier/prettier": "warn"
}
}

Finally, insert this to your package.json to create lint command, which runs ESlint.

"scripts": {
"lint": "eslint src/**/*.js"
}

Optionally you can create .prettierrc file to tweak Prettier options. I prefer this setup:

{
"quotes": "single",
"quoteProps": "consistent"
}

Now if you run yarn lint it should look like this:

Source code

My library is going to be a set of utilities to generate random values. First, let’s install the dependencies we are going to need:

(polished is a toolkit for writing styles in javascript, we are going to use their darken and lighten functions)

yarn add polished

We are going to put all of our source files in the src folder. Let’s start with a simple integer and a float generator:

number.js

Then add random color generators:

color.js

Finally, create an index.js file to export everything you want to be accessible to the outside world.

index.js

Bundle code with Rollup.js

You should bundle your code into a single file to remove unnecessary code, hide internal APIs, and make your code ES5 compatible.

It’s also a good practice to compile your package to CommonJS, ESM, and UMD.

What are CommonJS, ESM, and UMD?

CommonJS, ESM, and UMD are module definitions. It’s the way Node and browsers import your modules.

  • CommonJS is a module definition introduced by and mostly used in Node.js and code bundlers like Webpack. You can’t use CommonJS natively in your browser.
    const a = require(“a”);
  • ESM (ECMAScript module) is the new standardized way to handle modules. They can be used in Node as well in a browser. (Although they are still experimental in Node and it’s better to use a bundler if you want to run code in a browser.) And they are ideal to use with bundlers!
    import { a, b } from “a”;
  • UMD tries to support Node as well as browsers, but it’s not ideal for usage with bundlers, but you should provide it for legacy reasons.

If you use Webpack in your projects, it first looks for ES modules (defined by the “module” in package.json), and then it fallbacks to CommonJS modules. What Rollup does is take all of your modules and combines them into one file, which consumers can import.

Setup

Let’s start with installing Rollup:

yarn install rollup

Then create this simple rollup.config.js file:

rollup.config.js

Also, add this to your scripts in package.json:

"build": "rollup -c"

To build your project, use:

yarn build

Minification

CommonJS and ESM modules shouldn’t be minified. Minification should be left to the end-user. For UMD build, on the other hand, you should provide a minified version of the build.

To achieve that, use a rollup-plugin-terser plugin:

yarn add -D rollup-plugin-terser

Then add a new input to your config file:

rollup.config.js

Now if you run the yarn build it should produce this:

Files produced by Rollup

Transpile code with Babel

Your package should be transpiled to ES5 to support older browsers (such as ie11). You don’t have to do this, but it’s a good practice to provide compatible code. The easiest way to transpile code is to use Babel.

Start with installing necessary dependencies:

yarn add rollup-plugin-babel @babel/core @babel/preset-env

Add .babelrc file to the root directory:

{
"presets": [
["@babel/preset-env", {
"modules": false
}]
]
}

We are using babel preset, which transpiles code into ES5 by default.

“module: false” means that babel should not transpile ESM imports and leave them up to Rollup.

Finally, update your Rollup config by adding Babel plugin to your inputs:

rollup.config.js

Add entry points of your package

Now you have compiled files but you also need to add entry points to the package.json:

"type": "module",
"main": "lib/index.js",
"module": "lib/index.es.js",
"jsnext:main": "lib/index.es.js",
"browser": "dist/index.js",
  • type indicates that the package uses ES modules.
  • mainis entry point for CommonJS.
  • module and jsnext:main are entry points for ES modules.
  • browser is entry point for UMD.

Test code with Jest

For testing the code, I prefer using Jest. Start by installing the required dependencies:

yarn add -D jest

Jest does not require any config to work out of the box, so you can start creating your tests.

Unfortunately, Jest does not support ES modules out of the box so I used CommonJS require.

Finally, add "test": "jest" to your scripts and use yarn test for running Jest.

Publishing the package

Before you publish your package you can provide additional info about your package in package.json.

{
"name": "@alfonz/random",
"private": false,
"version": "0.0.1",

"author": "Denis Homolik",
"license": "MIT",
"description": "Simple package of random related utilities",

// it's a good practice to use "sideEffects": false if your package doesn't have side effects
"sideEffects": false,

// list of all files that will be included in the package (all files are included if omitted)
"files": [
"dist",
"lib"
],

// relevant keywords for your package
"keywords": [
"random"
],
[...]
}

To publish your package to npm, you need an npm account, which is free unless you want to publish private packages. Once you have that you need to log in with:

npm login

Then simply publish the package with:

npm publish --access public

Now you should be able to create packages others can use with ease. Thank you for reading :)

npm package: https://www.npmjs.com/package/@alfonz/random

Github repo: https://github.com/AlfonzAlfonz/random

--

--