package.json Scripts
npm lifecycle scripts automate build, test, and publish steps. They are the contract between developers, CI, and deployment pipelines.
Search across all documentation pages
npm lifecycle scripts automate build, test, and publish steps. They are the contract between developers, CI, and deployment pipelines.
Quick-reference recipe card - copy-paste ready.
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.build.json",
"start": "node dist/index.js",
"test": "node --import tsx --test test/**/*.test.ts",
"typecheck": "tsc --noEmit",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run typecheck"
}
}When to reach for this:
npm test) that works locally and in CI.npm publish must compile TypeScript before consumers import the package.{
"name": "@acme/billing-api",
"version": "1.4.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": ["dist"],
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc -p tsconfig.build.json",
"start": "node dist/server.js",
"test": "node --import tsx --test",
"typecheck": "tsc --noEmit",
"lint": "eslint .",
"prepare": "npm run build",
"prepublishOnly": "npm run lint && npm test && npm run typecheck",
"postversion": "git push && git push --tags"
}
}# Local development
npm run dev
# CI pipeline (same entry points)
npm ci
npm run lint
npm run typecheck
npm test
npm run buildWhat this demonstrates:
dev uses tsx watch for fast TypeScript iteration without a separate compile step.prepare builds on npm install when the package is installed from git or packed tarball.prepublishOnly runs only before npm publish, catching regressions before they hit the registry.postversion automates tag pushes after npm version patch.npm run <script> or npm test for the test alias.prepare, prepublishOnly, postinstall) fire automatically at defined moments.PATH with node_modules/.bin prepended, so local CLIs resolve without npx.npm run sets npm_lifecycle_event so scripts can branch on the triggering hook.| Hook | When it runs | Typical use |
|---|---|---|
prepare | After npm install (local and packed) | Compile TypeScript, generate types |
prepublishOnly | Before npm publish | Tests, lint, build verification |
postinstall | After dependency install | Native addon builds (use sparingly) |
preversion / postversion | Around npm version | Changelog, git tag push |
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.build.json",
"typecheck": "tsc --noEmit -p tsconfig.json"
}
}typecheck separate from build so CI can fail fast without emitting dist/.tsx for dev; ship compiled JS in dist/ for production node dist/....prepare calling npm install or reinstalling triggers itself. Fix: only compile or copy files in prepare.npm run setup.npm publish --ignore-scripts bypasses prepublishOnly. Fix: enforce CI publish from tagged commits with script checks.NODE_ENV=production cmd fails on Windows shells. Fix: use cross-env or framework-specific flags.prepare, which can surprise consumers. Fix: publish prebuilt artifacts to npm instead of git URLs for libraries.| Alternative | Use When | Don't Use When |
|---|---|---|
Makefile / just | Polyglot repos, ops-heavy workflows | Team expects npm test everywhere |
| Turborepo task pipeline | Monorepos with cached build/test | Single-package API service |
| Husky + lint-staged | Pre-commit formatting only | Replacing CI gates entirely |
prepare runs on npm install (including git dependencies) and before pack/publish.prepublishOnly runs only immediately before npm publish.prepare for builds consumers need; use prepublishOnly for publish-only checks.{
"scripts": {
"check": "npm run lint && npm run typecheck && npm test"
}
}Chain with && so later steps skip if an earlier step fails.
npm test -- --grep "billing"Arguments after -- are forwarded to the underlying command.
start should run node dist/... after build.tsx watch via a separate dev script.prepare runs after install when the package is installed from git or a local path. Publish built dist/ to npm or document the compile step.
Use --ignore-scripts only when you fully control what is skipped. Prefer explicit npm run build in CI instead of relying on side effects.
Yes: "check": "npm run lint && npm run test". npm resolves local binaries automatically.
preversion (optional), then version bump, then postversion. Use postversion to push tags.
Prefer devDependencies (tsx, eslint) and bare command names (tsx, eslint) so versions are lockfile-pinned.
Root package.json delegates: "test": "turbo run test". Each workspace keeps its own build/test scripts.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 18, 2026