Publishing Your First NPM Package

This guide walks you through the complete process of publishing your first NPM package, drawing from my experience publishing the following packages:

@qzi/hexo-word-counter : switch to wasm and enhance the bun compatible
@qzi/hexo : enhance the bun compatible and cloudflare pages support of hexo
hexo-theme-tweet : a concise twitter-like theme for hexo

Understanding the NPM Publish Lifecycle

---
config:
  theme: 'base'
  flowchart:
    nodeSpacing: 5
    rankSpacing: 15
    padding: 5
    diagramPadding: 5
  themeVariables:
    primaryColor: 'transparent'
    primaryTextColor: '#c9d1d9'
    primaryBorderColor: '#767677ff'
    lineColor: '#6a9fb5'
    secondaryColor: 'transparent'
    tertiaryColor: 'transparent'
    mainBkg: 'transparent'
    nodeBorder: 'transparent'
    edgeLabelBackground: 'transparent'
    edgeLabelColor: '#c9d1d9' 
    labelBackground: 'transparent'
    labelBoxBkgColor: 'transparent'
    labelBoxBorderColor: 'transparent'
    clusterBkg: 'transparent'
    fontSize: '18px'
title: Lifecycle of Npm Publish 

---
graph TB
    prepublish --> prepare
    prepare --> prepublishOnly
    prepublishOnly --> prepack
    prepack --> postpack
    postpack --> publish
    publish --> postpublish

Key Lifecycle Scripts

  • prepare: Runs before npm publish and on local npm install (without arguments). This is the best place for build steps (e.g., compiling TypeScript, building WASM) that need to run before the package can be used.
  • prepublishOnly: Runs only before npm publish. Use this for steps that should solely happen before publishing, such as running tests (npm test) or linting (npm run lint).

Publishing Your First Package

In this section:

  • Login to NPM Registry
  • Publishing Your First NPM Package

Login to NPM Registry

Before publishing, you need to authenticate with the NPM Registry. This is a one-time setup that stores your credentials locally.

1
2
3
4
5
6
7
❯ npm login
npm notice Log in on https://registry.npmjs.org/
Login at:
https://www.npmjs.com/login?next=/login/cli/***
Press ENTER to open in the browser...

Logged in on https://registry.npmjs.org/.

Publishing Your First NPM Package

Publish your public package to the NPM Registry for free.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ npm publish --access public

> @qzi/hexo-word-counter@0.2.2 prepublishOnly
> npm run build
...
[INFO]: ✨ Done in 4.30s
[INFO]: 📦 Your wasm pkg is ready to publish at /Users/leon/repos/hexo-word-counter/pkg.

> @qzi/hexo-word-counter@0.2.2 prepare
> npm run build
...
[INFO]: ✨ Done in 4.01s

npm notice
npm notice 📦 @qzi/hexo-word-counter@0.2.2
npm notice Tarball Contents
npm notice ...
npm notice integrity: sha512-rNSsYRowndbHY[...]68toM/oFUWD3w==
npm notice total files: 5
npm notice
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access
+ @qzi/hexo-word-counter@0.2.2

Unpublish Your Published NPM Package

NPM Registry allows you to unpublish specific versions of your package.

1
❯ npm unpublish @qzi/hexo-word-counter@0.2.2 --force

Patch Versioning

Patch versioning is used to release bug fixes.

1
2
3
# patch versioning from v0.1.0 to v0.1.1
❯ npm version patch
v0.1.1

Dry Run NPM Command

Dry run is a feature that allows you to test a command without actually running it.
We can use it to test the package command without actually packaging before publishing.

--dry-run
Default: false
Type: Boolean
Indicates that you don’t want npm to make any changes and that it should only report what it would have done. This can be passed into any of the commands that modify your local installation, eg, install, update, dedupe, uninstall, as well as pack and publish.

1
❯ npm pack --dry-run 2>&1

NPM Package Name Aliasing

npm install <alias>@npm:<name>

Installing a package under a custom alias allows you to:

  • Use multiple versions of the same package side-by-side.
  • Create shorter names for packages with long import paths.
  • Replace dependencies easily with git forks or forked npm packages.

Note: Aliasing only works on your project and does not rename packages in transitive dependencies. Aliases should follow the naming conventions stated in validate-npm-package-name.

Examples:

1
2
3
4
5
6
7
8
# package.json
"dependencies": {
# alias @qzi/hexo to hexo, npm: is just a syntax marker, unrelated to the npm command
"hexo": "npm:@qzi/hexo@^7.3.0",
...
"hexo-word-counter": "npm:@qzi/hexo-word-counter@^0.2.8"
},

Then in a direct dependency of your project, you can import the package like this:

1
2
// Your code imports from "hexo", but it actually uses @qzi/hexo under the hood
import { ... } from "hexo"; // not @qzi/hexo

References