npm runs pre<script> before and post<script> after any script automatically:
{"scripts":{"prebuild":"npm run clean","build":"tsc","postbuild":"npm run copy-assets","clean":"rm -rf dist","copy-assets":"cp -r assets dist/","prepare":"husky","prepublishOnly":"npm run build && npm test","postinstall":"patch-package"}}
Built-in lifecycle scripts:
prepare -- runs after npm install and before npm publish
prepublishOnly -- runs before npm publish only (not on install)
Glob patterns: run-p lint:* runs all scripts matching lint:
Environment Variables
{"scripts":{"build:staging":"cross-env NODE_ENV=staging API_URL=https://staging.api.com vite build","build:prod":"cross-env NODE_ENV=production vite build"}}
npm exposes package.json fields as npm_package_* (e.g., npm_package_name, npm_package_version).
npx and bunx
npx create-next-app@latest my-app # run without installing
npx -p typescript tsc --version # specify package explicitly
npx vitest # run local binary from node_modules/.bin
bunx create-next-app@latest my-app # Bun equivalent (faster)
Dependency Management
npm install # all deps from package.json
npm install lodash # add production dep
npm install -D typescript # dev dependency
npm install lodash@4.17.21 # exact version
npm install user/repo # from GitHub
npm ci # clean install from lock file (CI)
npm outdated # list outdated packages
npm update # update within semver ranges
npx npm-check-updates -u # update package.json beyond ranges
npm dedupe # remove duplicate packages
npm why lodash # show why a package is installed
npm ls lodash # find installed versions
npm audit # check vulnerabilities
npm audit fix # auto-fix compatible vulnerabilities
npm audit --omit=dev # audit production deps only
# ALWAYS commit lock files to version control
npm ci # deterministic install, fails if lock out of sync
npm ci --ignore-scripts # skip lifecycle scripts
pnpm install --frozen-lockfile # pnpm equivalent
yarn install --immutable # yarn equivalent
Regenerate only when resolving deep conflicts or migrating package managers: delete node_modules and lock file, then npm install.
npm install # install all (hoisted to root)
npm run build -w packages/shared # run in specific workspace
npm run build -w @myorg/shared # by package name
npm run test --workspaces # run across all workspaces
npm run build -ws --if-present # skip workspaces missing the script
npm install zod -w packages/shared # add dep to workspace
npm install @myorg/shared -w apps/web # add workspace as dependency
Monorepo Root Scripts
{"scripts":{"build":"npm run build --workspaces --if-present","test":"npm run test --workspaces --if-present","lint":"npm run lint --workspaces --if-present","dev:web":"npm run dev -w apps/web","dev:api":"npm run dev -w apps/api"}}
npm hoists shared deps to root node_modules. Conflicts stay in the workspace's own node_modules. Use npm dedupe if duplication creeps in.
pnpm
pnpm install # install all
pnpm add lodash # add dep
pnpm add -D typescript # dev dep
pnpm dlx create-next-app # like npx
pnpm -F web run build # filter by name
pnpm -r run build # all workspaces
pnpm -r --parallel run dev # all in parallel
pnpm -F web... run build # web and its dependencies
yarn
yarn add lodash # add dep
yarn add -D typescript # dev dep
yarn dev # run script (no 'run' needed)
yarn dlx create-next-app # like npx (yarn berry)
yarn workspace web build # workspace command
yarn workspaces foreach -A run build
Publishing
Package Setup
{"name":"@scope/package","version":"1.0.0","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"}},"files":["dist","README.md","LICENSE"],"publishConfig":{"access":"public"},"scripts":{"prepublishOnly":"npm run build && npm test"}}
The files field whitelists what goes in the tarball. Prefer files over .npmignore.
Publish and Version
npm publish # publish to registry
npm publish --access public # scoped packages (first time)
npm publish --dry-run # preview what gets published
npm pack --dry-run # list files that would be included
npm publish --provenance # with build provenance (CI only)
npm version patch # 1.0.0 -> 1.0.1
npm version minor # 1.0.0 -> 1.1.0
npm version major # 1.0.0 -> 2.0.0
npm version prerelease --preid=beta # 1.0.0 -> 1.0.1-beta.0
npm version auto-updates package.json, creates a git commit, and tags it. Hook into the flow:
npm run build --verbose # see exact command
npm run build --silent # suppress npm output, show only script output
npm run # list all available scripts
node --inspect-brk dist/index.js # debugger, break on first line
NODE_OPTIONS='--inspect' npm run dev # attach inspector to any script
DEBUG=express:* npm run dev # debug logging (common pattern)
NODE_DEBUG=module node dist/index.js # debug module resolution
Use npm ci (not npm install) in CI for reproducible builds
Set save-exact=true in .npmrc for critical dependencies
Use npm publish --provenance for public packages
Never store auth tokens in committed .npmrc -- use environment variables
Quick Reference
npm init -y # create package.json
npm cache clean --force # clear cache
npm config list # show config
npm exec -- eslint . # run local binary
npm link# symlink package for local dev
npm repo # open repo in browser
npm docs lodash # open package docs
npm view lodash versions # list published versions