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.
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.
- In the root directory of my-package, run:
npm link
- Navigate to your test project directory and run:
npm link my-package
Note that the link should be to the package name specified in the package.json file, not the directory name for that package.
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.
- 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();
- 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
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.
Option 3: Github Actions is the best.
Option 1: .npmrc
file
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
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:
//...
"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
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"
}
}
Comments
Recent Work
Basalt
basalt.softwareFree desktop AI Chat client, designed for developers and businesses. Unlocks advanced model settings only available in the API. Includes quality of life features like custom syntax highlighting.
BidBear
bidbear.ioBidbear is a report automation tool. It downloads Amazon Seller and Advertising reports, daily, to a private database. It then merges and formats the data into beautiful, on demand, exportable performance reports.