How to Publish NPM Packages
If you find yourself re-using code in a range of projects, an npm package can be an elegant method of isolating and distributing that code throughout your codebase. Once published, your packages can be installed and updated via the npm CLI. Not only this, publicly hosting your npm packages can attract the developer community to contribute to your code and expand your package’s capabilities and sophistication.
We will explore the npm publishing process in this article, as well as standard practices such as versioning, providing a readme and licence file, scoping packages, bumping versions on subsequent publishing, and more.
Scoped and Unscoped Packages Explained
Npm gives us the capability to group a multitude of packages under a common name, called a “scope”. Every npm user or organisation has a scope where packages can only be added by you — the owner. For example, I could publish
@rossbulat/<package_name2> under my
This gives us benefits such as housing related packages under one branch, whilst also allowing us to create packages of the same name that may exist under an unscoped or another scoped project. For example,
@rossbulat/react is a perfectly legal package name, even though
react already exists and is getting 6 million weekly downloads at the time of writing.
We will explore how to publish scoped packages further down. Let’s jump straight into setting up a public npm package.
Setting up an NPM Package
There are only a couple of pre-requisites needed for us to publish a package:
- Node and npm installed on your system. The npm package manager comes bundled with Node, therefore download the latest version or use your favourite package manager, for example:
brew install node
- A Github account (or any git client, Bitbucket being another) to manage your package repositories.
- The latest version of npm. Run
sudo npm i -g npm@latestto ensure you are on the latest version.
- An npm account. You will notice upon signing up to npmjs that we have a choice of plans to subscribe to. For the purposes of this talk the free plan is more than adequate; you can still publish packages under your username or with an organisation, given that they are public packages.
Note: It is common practice, almost an unofficial policy, of the open source community to provide their tools for free for public repositories, with tiers for private repositories, the pricing of which depending on the amount of private repositories you require, on a per-user basis. Having access to the greatest tools for free promotes the evolution of the open source ecosystem where everyone benefits from public, transparent codebases.
Once you have signed up to npmjs.com, go ahead and login on your machine via the Terminal:
npm login#fill out username, password and email address prompts
Now check that you have successfully logged in, with the
whoami command. Also check the
We are now logged in and able to interact with our npm account. For additional authentication settings such as 2FA, check out this page.
Setting npm defaults
Let’s briefly explore the default configs and change a couple of settings. Run the following to see the initial configurations:
npm config list --json
Quite a lot of settings are displayed here, the majority of which are not relevant for this talk, but it is always beneficial to familiarise yourself with everything possible with npm (we are experts after all). However, there are some particular fields of interest here:
We can give npm author credentials that will be referred to when publishing. These details are used for identifying package authors along with some contact details. Go ahead and configure these now, here are mine:
npm set init.author.name "Ross Bulat"
npm set init.author.email "email@example.com"
npm set init.author.url "https://rossbulat.co"
If you’d like to explore npm configuration further at this point, run
npm help config to get detailed instructions directly from your terminal.
Explore npm with the help command
The help command can be applied throughout npm, and is fun to explore in the event you have some time to spare, perhaps at an airport waiting for a flight.
Grab all the commands available and dive deeper into each one to find out more about npm’s functionalities:
# get all npm commands on offer
npm --help# detailed documentation on particular command usage
npm help <command># shortened list of sub-command usage
npm <command> --help
Ok, with your author details set npm now knows who is publishing packages. But there is one more factor to consider — whether to publish your package as an individual or organisation.
Publish under your username or under an Org?
At this stage decide whether you want to house your packages under your username or an organisation, or “Org”. An npm Org allows you to allocate other npm users as part of your team, who then can have access rights configured to view, download and write (publish / update) packages.
All packages under an Org will be scoped to that Org: Orgs can only publish scoped packages — all of which will fall under the
@org_name/<package_name> convention. If you wish to create an Org for this purpose, you can do so from your npmjs homepage — npmjs have a handy guide to do so here.
For a comprehensive understanding, it is also worth reviewing the visibility of packages depending on the scope and access levels here.
Setting up a Package
A package can be initialised from an empty directory or from an existing git project. In the event you wish to clone a repository before initiating a package, create a new directory and fetch the remote git repo:
git remote add <remote_url>
In the event you are starting from scratch, simply create an empty directory for your package. Now run the
npm init command to generate a
package.json file with the corresponding details you provide.
For an unscoped package, simply running
npm init is all that is needed:
npm init# fill out details or leave blank if they do not apply
For a scoped package, run the same command with the
--scope flag to specify a scope, being either your username or one of your Orgs:
# scope package under my username
npm init --scope "@rossbulat"# or scope package under an Org
npm init --scope "@jkrb"
Note: did you notice an empty
scope field when we ran
npm config list --json earlier? You could have indeed set a default scope by running
npm set init.scope "@rossbulat". The flags used here with
npm init allow us to override our defaults, in this case providing a custom scope.
Ready to publish? — Not yet
From here we could go ahead and publish the package, with
npm publish. However, before we do let’s cover some standards and critical files you may wish to include in your packages.
Choosing a version
Semantic versioning, or SemVer, is widely used to choosing a version of a package, but ultimately it is your decision on what standard you adopt for versioning your package. With SemVer we provide 3 numbers separated with periods to version a package, in the form of
- Major releases will most likely involve breaking changes to the package, where upgrading users will need to make adjustments to the package integration — prior warnings to releasing a major package should be published, as well as a changelog that clearly documents the breaking changes as a reference for the developer community.
- Minor releases will mostly involve adding functionality in a backwards — compatible manor, whereby no breakages occur.
- Patches mostly pertain to small amendments to the current codebase, that may involve backwards-compatible bug fixes, code tidy-up or refactoring.
Choosing a Licence
Including a licence within your package is common practice, and a necessity: it is encouraged not to contribute to a project unless a licence is present, so as a developer you know where you stand. To save repetition, Github provide a great reference document outlining the most popular licences — that can be found here.
Take note of the commonly used licence response object. Each licence is linked to a Github repository that outlines the purpose of the licence, the rights it gives to the public domain, and how to implement the licence within your repository. One example of a commonly used licence is the Unlicenced licence.
A couple of notes when implementing your licence:
- Including a licence within repositories are commonly saved as
- Make reference to your licence within your README document, clearly outlining which licence you are adopting.
- It is also common to include a header comment with all of your repository files, whether they be
.tsx, etc... One of the current projects I am working on adopts the Apache licence, which is reflected on every file with the following comment:
/* Copyright 2017-2019 @polkadot/apps authors & contributors
/* This software may be modified and distributed under the terms
/* of the Apache-2.0 license. See the LICENSE file for details. */
There are tools available to quickly implement such a comment. If you are using the Atom IDE, the file-header package. A similar package is also available for Sublime Text in the form of the FileHeader package.
Your readme file will most likely be a markdown formatted document, therefore the common readme filename is
README.md. The README file should outline the project name and purpose as an overview, and include prerequisites, installation and getting started instructions, as well as testing and contribution guides.
To cut down on detailed documentation, README files should provide links to more detailed API references and documentation where necessary. Check out this README Template as a reference of how to write a well structured README document.
Add badges to your README using Travis CI
Travis CI is dubbed as a continuous integration tool. The service builds and tests your packages on every commit to a Github repository automatically, easing the work of contributors. Not only this, the service provides badges (or status images as Travis have termed them) directly into your README file.
Badges reflect the status of your repository, the most common of which being the Build Status of your repository, reflecting whether the package successfully builds. Learn more about Travis badges and embedding them into your README here.
In the event you would like to onboard the developer community into your project, it could be beneficial to provide a dedicated CONTRIBUTING.md document. A good example of such being Babel’s version, that can be viewed here.
.npmignore with .gitignore
We have the option to use
.npmignore to ignore files for publishing. If there is no
.npmignore but there is a
.gitignore file present, then
.gitignore will be referenced instead.
.gitignore. If you wish to publish files within
.gitignore, supply an
.npmignore file to overwride the former. Check out the “Keeping files out of your package” section of the npm documentation for more information on this.
Before Publishing: Test the package
Run the following within your package directory to test if your package successfully builds:
npm install . -g
npmjs have an entire section on pre-publishing, running tests to ensure that your package builds successfully.
Publishing and Updating Packages
npm publish to upload your package to the npm public registry:
If we are updating an existing package, all we need to do is bump the version in
package.json and run
npm publish again. We could indeed hardcode the new version within
package.json, or use my preferred method of utilising another npm command:
npm version <update_type>.
Updating with npm version
npm version takes a range of string values, each adhering to the SemVer versioning standard and bumping the version number in various ways. We can also supply an optional commit message with a version bump before publishing, with
# major bump. E.g. 1.0.0 to 2.0.0
# %s here being the version number
npm version major -m "major release v%s"# or minor bump. E.g. 1.0.0 to 1.1.0
npm version minor# or patch bump. E.g. 1.0.0 to 1.0.1
npm version patch -m "bug fixes"# publish
npm help version, you will see that we have more options to play with, including
prerelease, allowing you to upload pre-releases to the community for testing.
Tip: We can also get the version from git, with
npm version from-git. This references the version tag from git that we can then utilise with npm, like so:
git add .
git commit -m "new release"
git tag v1.1.0
git push origin master --tagsnpm version from-git
We have now covered the publishing process in some detail to the public npm registry (the registry URL is actually another configurable option — check again with
npm config list --json).
For further reading on publishing, npmjs have comprehensive documentation on the processes and capabilities of the platform. Dive deeper into it from the Packages and Modules official npmjs documentation.
Where to go from here?
This talk was purely focused on the public npm registry. There is indeed a private registry option included with paid plans on npmjs, but a more attractive solution for many developers is the idea of hosting a private proxy registry, whereby you can host packages privately within a separate registry while maintaining access to the public npm registry at the same time.
I will explore setting up such a solution, with a package named Verdaccio, in a future insight. For now, I hope this walkthrough gave clarity on the public publishing process.