Skip to main content

NPM Package Creation

Introduction

I've always sort of had this impression that creating an npm package was something that only wizards did. But recently a user of one of my public repos suggested that I publish it as an npm package. So here we are.

The steps to publish an npm package are basically this:

Package Creation Steps

1. Setup Project

Create a new directory for the package/Project

mkdir my-package
cd my-package

Use npm to create a package.json file.

npm init

Follow the prompts to fill in details like package name, version, description, entry point, etc.

tip

You have probably seen a package that is part of a larger group like this @babel/core. This is called a scoped package. Scoped packages are useful for grouping related packages together.

To create a scoped package, you can use the --scope flag with npm init.

npm init --scope=my-scope

Read more about this here: Scoped Packages

2. Write Package Code

Write the code for your package in the directory. For example, create an index.js file with your module's functionality.

// index.js
module.exports = function () {
console.log('Hello, world!');
};

3. Document the Package

  • Create a README.md file to document your package. This is important for users to understand how to use your package.

  • Create a LICENSE file to specify the license under which your package is distributed.

4. Testing the Package

📘 npm link

You can use npm link to test your package locally before publishing it.

  1. In the root directory of my-package, run:
npm link
  1. Navigate to your test project directory and run:
npm link my-package
tip

Note that the link should be to the package name specified in the package.json file, not the directory name for that package.

warning

If you are using a test project that is dockerized, and normally run with docker compose up or something similar, the symlink will not work. You will need to run the test project on your local environment (npm run start/dev/whatever), or work some magic to symlink the package inside the docker container.

  1. In your test project, you can now use my-package as follows:
const myPackage = require('my-package');
// Or if using ES6 imports
import myPackage from 'my-package';

// Use your package's functionality
myPackage.someFunction();
  1. Once you are done testing and want to remove the link, run the following in your test project directory:
npm unlink my-package

and optionally in your package directory:

npm unlink

if you want to see all of the packages that are linked to your project, you can run:

npm ls --link

Automated Tests

Write automated tests to ensure your package works as expected. You can use testing frameworks like Mocha, Jest, etc.

5. Publish the Package

Login to npm

npm login

Publish the package

npm publish

6. Versioning

When you make changes to your package, you should update the version number in the package.json file. You can use the npm version command to do this.

npm version <update_type>

Where <update_type> is one of patch, minor, or major.

Deprecate/Unpublish

Deprecate Versions: If needed, you can deprecate old versions of your package.

npm deprecate my-package@"<version>" "message"

Unpublish: If you need to remove a package from the registry, you can use the npm unpublish command.

npm unpublish my-package@<version>

Package Naming Convention

When naming your package, it's important to follow the naming conventions. Some common conventions are:

  • Use lowercase letters
  • Use hyphens to separate words
  • Do not use spaces or special characters

For example, my-package is a valid name, while MyPackage or my_package are not.

Readme Badges

The badges that you see in the README files of many packages are generated by services like shields.io. You can create your own badges by visiting their website: Shields.io

Here are some of my favorite badges:

Adding the Package to the Github Package Repository

📘 Github Package Registry

tip

To publish a package to the Github Package Repository, the package must be scoped/namespace such as @OWNER/package-name. In my case I use @cascadia-code for my packages.

tip

Option 3: Github Actions is the best.

Option 1: .npmrc file

danger

If you use a .npmrc file, be sure to add it to your .gitignore file so that it is not committed to your repository, potentially exposing your Github token.

Consider instead using Github Actions to publish your package with Github Secrets to manage your token variables in a secure environment.

There is a way to configure all this with a new file in the project directory called .npmrc. This file will contain the following:

//npm.pkg.github.com/:_authToken=TOKEN
@NAMESPACE:registry=https://npm.pkg.github.com

Replace TOKEN with your Github token (classic) and OWNER with your Github username. I'm not a huge fan of this method however as this opens up the possibility of accidentally committing your github token to a public repository (that's mucho bad).

Option 2: package.json + Command Line

warning

Nevermind, I could not get option 2 to work to save my life. I'm going to stick with the .npmrc file. It sounds like Github Actions can accomplish this also and then your Token can be stored in Github Secrets?

You can also add the following to your package.json file:

package.json
//...
"publishConfig": {
"access": "public",
"registry": "https://npm.pkg.github.com"
},
//...

Which tells npm that we want the package to be public and also to publish the package to the Github Package Repository.

To authenticate by logging in to npm, use the npm login command, replacing USERNAME with your GitHub username, TOKEN with your personal access token (classic), and PUBLIC-EMAIL-ADDRESS with your email address.

Option 3: Github Actions

tip

This appears to be the preferred method from both a security standpoint, as well as actually just working at all. The above methods did not work for me.

📘 Publishing and installing a package with GitHub Actions

package.json sample file

Here is a sample package.json file that i've used to publish a package.

{
"name": "@cascadia-code/scroll-to-hash-element",
"version": "1.0.1",
"description": "Enables hash links. Listens to the hash property of React-Router location and scrolls to the identified element if it exists.",
"main": "src/index.tsx",
"publishConfig": {
"access": "public"
},
"scripts": {
"test": "npm test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ncoughlin/scroll-to-hash-element.git"
},
"keywords": ["react-router", "hash", "hash-link"],
"author": "Nicholas Coughlin",
"license": "MIT",
"bugs": {
"url": "https://github.com/ncoughlin/scroll-to-hash-element/issues"
},
"homepage": "https://github.com/ncoughlin/scroll-to-hash-element#readme",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-router-dom": "^6.0.0"
},
"devDependencies": {
"@types/react": "^18.3.3",
"typescript": "^4.9.0"
}
}

Automated Amazon Reports

Automatically download Amazon Seller and Advertising reports to a private database. View beautiful, on demand, exportable performance reports.

bidbear.io