|$ curl https://forge-ai.dev/api/markdown?path=docs/npm/lifecycle
$cat docs/npm-—-package-lifecycle-&-hooks.md
updated Recently·10 min read·published

npm — Package Lifecycle & Hooks

npmIntermediate
Introduction

npm provides a powerful system of lifecycle scripts that run automatically at specific points during package installation, publishing, and development. Understanding the lifecycle is crucial for setting up build steps, database migrations, and other automation.

This guide covers lifecycle script order, pre/post hooks, npm link for local development, npx, and npm cache management.

Lifecycle Script Order

npm runs lifecycle scripts in a specific order when you run npm install, npm publish, and other commands. Understanding this order helps you choose the right hook for your automation.

OrderScriptRuns During
1preinstallBefore installation starts
2installAfter preinstall, during installation
3postinstallAfter installation completes
4prepublishBefore packing and publishing (deprecated)
5prepareBefore packing, after npm install, on git pull
6prepublishOnlyBefore publishing (replaces prepublish)
7prepackBefore tarball is created
8postpackAfter tarball is created
9publishAfter publish completes
10postpublishAfter publish completes
terminal
Bash
1# npm install lifecycle:
2# 1. preinstall
3# 2. install
4# 3. postinstall
5# 4. prepare (if git or npm link)
6
7# npm publish lifecycle:
8# 1. prepublishOnly
9# 2. prepare
10# 3. prepack
11# 4. pack (creates tarball)
12# 5. postpack
13# 6. publish
14# 7. postpublish
15
16# npm pack lifecycle:
17# 1. prepack
18# 2. pack
19# 3. postpack
Pre/Post Hooks

npm automatically provides pre and post hooks for any script you define. If you create a script named build, npm will automatically run prebuild before it and postbuild after it.

package.json
JSON
1{
2 "scripts": {
3 "prebuild": "rimraf dist",
4 "build": "tsc",
5 "postbuild": "cp package.json dist/",
6 "pretest": "eslint src/",
7 "test": "vitest run",
8 "posttest": "echo 'Tests complete'",
9 "predeploy": "npm run build",
10 "deploy": "aws s3 sync dist/ s3://my-app",
11 "postdeploy": "npm run healthcheck"
12 }
13}
14
15// Running npm run build executes:
16// 1. prebuild -> rimraf dist
17// 2. build -> tsc
18// 3. postbuild -> cp package.json dist/
19
20// Running npm test executes:
21// 1. pretest -> eslint src/
22// 2. test -> vitest run
23// 3. posttest -> echo 'Tests complete'

info

You can also create pre/post hooks for any custom script: premyscript runs before myscript, and postmyscript runs after. This works for any script name, not just built-in ones.
prepare & prepublishOnly

The prepare and prepublishOnly scripts are often confused. They serve different purposes:

ScriptWhen It RunsCommon Use
preparenpm install, npm pack, npm publish, git pull (with husky)Build step, install husky hooks, compile TypeScript
prepublishOnlyOnly during npm publishRun tests, lint, verify before publishing
package.json
JSON
1{
2 "scripts": {
3 "prepare": "husky && tsc",
4 "prepublishOnly": "npm test && npm run lint"
5 }
6}
7
8// prepare runs during:
9// - npm install (after dependencies are installed)
10// - npm link (after symlink is created)
11// - npm publish (before prepublishOnly)
12// This makes it ideal for:
13// - Setting up Git hooks (husky)
14// - Building TypeScript to JavaScript
15// - Generating browser bundles
16
17// prepublishOnly runs only during:
18// - npm publish (after prepare, before packing)
19// Use it for:
20// - Running test suite
21// - Linting
22// - Security audit
23// - Any check that should block publishing

best practice

Use prepare for build steps that need to run both during development (npm install) and before publishing. Use prepublishOnly for checks that should only run when publishing, like tests and linting.
npx — Run Without Installing

npx (bundled with npm 5.2+) executes Node packages without globally installing them. It downloads the package to a cache, runs it, and discards it — keeping your system clean.

terminal
Bash
1# Run a one-off command without installing
2npx create-react-app my-app
3npx tsc --init
4npx prettier --write src/
5
6# Use a specific version
7npx cowsay@1.5.0 "Hello"
8
9# Run from a local package
10npx mocha tests/
11
12# Check if npx is available
13npx --version
14
15# Where npx caches packages
16ls ~/.npm/_npx/
17
18# Force re-download (skip cache)
19npx --ignore-existing create-react-app my-app
20
21# npx is great for:
22# - One-off tools (create-react-app, degit)
23# - Running different versions of the same tool
24# - CI scripts without global installs
25# - Trying a package before committing to it
26
27# npm 9+ alias:
28npm x cowsay "Hello" # same as npx
🔥

pro tip

Use npx for any CLI tool you don't use regularly. This avoids global package clutter and version conflicts. For tools you use daily (like tsc or eslint), install them as devDependencies in your project and use npx to run them.
npm cache Management

npm caches downloaded packages to speed up subsequent installations. The cache is stored in ~/.npm/_cacache/ and can be verified, cleaned, and inspected.

terminal
Bash
1# Verify cache integrity
2npm cache verify
3# Rebuilds cache index and verifies all cached tarballs
4
5# Clean the entire cache
6npm cache clean --force
7# WARNING: This removes everything. Next install will be slow.
8
9# Cache location (macOS/Linux):
10ls ~/.npm/_cacache/
11
12# Cache content verification:
13npm cache ls
14# Lists all cached packages
15
16# Check cache size:
17du -sh ~/.npm/_cacache/
18
19# Cache behavior:
20# - npm stores tarballs in content-addressable storage
21# - Cache is content-addressable (same tarball = one copy)
22# - npm ci uses cache heavily for fast installs
23# - Cache is self-healing (npm cache verify fixes issues)
24
25# Best practice: Don't clear the cache unless
26# you're debugging corruption issues.
27# Use npm cache verify instead of cache clean.

info

Run npm cache verify periodically to prevent corruption. If you encounter mysterious package installation errors, always run npm cache verify before resorting to npm cache clean --force.
npm ci for Reproducible Builds

npm ci (clean install) is designed for CI/CD environments. It installs dependencies directly from package-lock.json without resolving ranges, making it faster and more deterministic.

terminal
Bash
1# CI install (fast, deterministic)
2npm ci
3
4# Key differences from npm install:
5# - Deletes node_modules before installing
6# - Uses package-lock.json exactly (no resolution)
7# - Fails if package-lock.json is out of sync with package.json
8# - Does not write to package-lock.json
9# - Faster (skips dependency resolution)
10# - Installs exact versions from lock file
11
12# Typical CI workflow:
13# 1. npm ci (restore dependencies exactly)
14# 2. npm run build
15# 3. npm test
16
17# CI is 2-10x faster than npm install for large projects
18# because it skips resolution and uses the cache.
19
20# If package-lock.json is missing or outdated:
21# npm ci will FAIL with an error message.
22# Solution: run npm install locally and commit the lock file.

best practice

Always use npm ci in CI/CD pipelines, not npm install. It is faster, more reliable, and ensures your CI build matches your local development environment. Commit package-lock.json to version control.
npm pack for Local Testing

npm pack creates a tarball of your package exactly as it would be published. This is useful for testing the publish artifact locally before actually publishing.

terminal
Bash
1# Create a tarball
2npm pack
3# Creates: my-package-1.0.0.tgz
4
5# Verify the tarball contents
6tar -tzf my-package-1.0.0.tgz
7
8# Install from tarball in another project
9npm install ../my-lib/my-package-1.0.0.tgz
10
11# Pack with a specific version
12npm pack --pack-destination ./output
13
14# Compare with npm publish dry-run:
15npm publish --dry-run
16# Shows what would be published without actually publishing
17
18# npm pack is useful for:
19# - Verifying package.json files field
20# - Checking .npmignore behavior
21# - Testing package size
22# - Local integration testing
23# - CI artifact caching
24
25# Check package size:
26npm pack --dry-run | wc -c
27ls -lh my-package-*.tgz
Best Practices

Use prepare for Build Steps

Put your build command in prepare to ensure it runs after npm install and before npm publish. This guarantees your package is always built.

Always Use npm ci in CI

npm ci is faster and more deterministic than npm install. It also catches the common mistake of forgetting to commit package-lock.json.

Don't Clear Cache Unless Necessary

The npm cache is content-addressable and self-healing. If you suspect corruption, run npm cache verify instead of npm cache clean --force.

npx Over Global Installs

Prefer npx for running one-off CLI tools. It keeps your global environment clean and ensures you always use the correct version for each project.

$Blueprint — Engineering Documentation·Section ID: NPM-LIFECYCLE·Revision: 1.0