DEV Community

Alex Chen
Alex Chen

Posted on

npm Scripts and package.json Mastery (2026)

npm Scripts and package.json Mastery (2026)

Most developers only use npm install and npm run dev. Here's everything else npm scripts can do for you.

package.json Deep Dive

{
  "name": "my-awesome-project",
  "version": "1.2.3",
  "description": "A well-configured project",

  // Scripts are where the magic happens
  "scripts": {
    // Basic scripts
    "start": "node server.js",
    "dev": "nodemon server.js",
    "build": "tsc && vite build",
    "test": "jest --coverage",

    // Pre/post hooks (auto-run before/after!)
    "pretest": "npm run lint",
    "postbuild": "npm run size-check",

    // Complex scripts
    "clean": "rimraf dist build .tsbuildinfo",
    "typecheck": "tsc --noEmit",
    "lint": "eslint 'src/**/*.{js,ts}'",
    "lint:fix": "npm run lint -- --fix",
    "format": "prettier --write 'src/**/*.{js,ts,json,md}'",
    "prepare": "husky install",

    // Deployment
    "deploy:staging": "NODE_ENV=staging npm run build && rsync -avz dist/ staging:/app/",
    "deploy:prod": "NODE_ENV=production npm run build && rsync -avz dist/ prod:/app/"
  },

  // Dependencies
  "dependencies": {
    "express": "^4.18.0",     // Runtime dependencies
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    "typescript": "^5.3.0",   // Development only
    "jest": "^29.7.0",
    "eslint": "^8.56.0"
  },

  // Important metadata
  "engines": {
    "node": ">=18.0.0",       // Required Node version
    "npm": ">=9.0.0"
  },
  "main": "dist/index.js",      // Entry point for require()
  "types": "dist/index.d.ts",   // Entry point for TypeScript
  "files": [                    // What gets published to npm
    "dist",
    "README.md"
  ],
  "bin": {                      // CLI commands
    "my-tool": "./bin/cli.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

Script Patterns You Need

# === Running Scripts ===

# Basic usage
npm run dev          # Runs the "dev" script
npm test             # "test" is special — can omit "run"

# Passing arguments
npm run test -- --grep "auth"       # Everything after -- goes to the script
npm run build -- --mode production

# Sequential execution
npm run clean && npm run build && npm run test

# Parallel execution (& is your friend)
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\""
# Or:
"dev": "npm-run-all --parallel dev:*"

# Cross-platform compatibility!
# ❌ Windows-incompatible:
"clean": "rm -rf dist"
# ✅ Use cross-platform tools:
"clean": "rimraf dist"           # rimraf works everywhere
"copy": "cp src dist"            # ❌ Not cross-platform
"copy": "cpx src dist"           # ✅ cpx or copyfiles

# Environment variables in scripts
"dev": "cross-env NODE_ENV=development nodemon server.js"
# cross-env handles Windows/Linux/Mac differences

# Script composition (DRY principle)
"prettify": "prettier --write '**/*.{js,css}'",
"lintify": "eslint src/**/*.js --fix",
"fixall": "npm run prettify && npm run lintify"
Enter fullscreen mode Exit fullscreen mode

Lifecycle Scripts (The Hidden Power)

{
  "scripts": {
    // These run automatically at specific times:

    "preinstall": "echo 'Installing...'",
    "postinstall": "node scripts/postinstall.js",
    // postinstall runs after every npm install!
    // Great for: generating files, setup checks, download binaries

    "prepublishOnly": "npm run typecheck && npm run lint && npm run test",
    // Runs BEFORE npm publish (not on npm install)
    // Safety net: prevents publishing broken code

    "preversion": "npm run test",
    "version": "git add -A && git commit -m 'release v%s'",
    "postversion": "git push && git push --tags",
    // Automate version bumping workflow!

    "pack": "npm pack --dry-run | head -n 20",
    // Preview what would be published without actually publishing
  }
}
Enter fullscreen mode Exit fullscreen mode

Managing Dependencies Like a Pro

# === Installation ===

npm install express         # Save to dependencies
npm install -D jest         # Save to devDependencies
npm install -g typescript   # Install globally
npm install express@latest  # Latest version
npm install express@^4.18.0 # Compatible version range

# === Version Ranges ===
# "^4.18.0" = >=4.18.0 <5.0.0  (compatible, most common)
# "~4.18.0" = >=4.18.0 <4.19.0  (patch updates)
# "4.18.0"  = exactly 4.18.0     (locked)
# "*"      = any version           (dangerous!)
# "next"   = next release         (cutting edge)
# "latest" = latest stable

# === Updating ===
npm outdated                 # Check for outdated packages
npm update                  # Update per package.json rules
npx npm-check-updates -u    # Update package.json to latest versions
                           # Then: npm install to apply

# === Auditing ===
npm audit                   # Check for vulnerabilities
npm audit fix               # Auto-fix if possible
npm audit fix --force       # Force fix (may break things, use carefully!)

# === Cleaning up ===
npm prune                   # Remove extraneous packages
npm dedupe                  # Reduce duplication

# === Analyzing Bundle ===
npm ls --depth=0            # Top-level deps only
npm ls express              # Why was this installed? (dependency tree)
du -sh node_modules         # How big is it?
npx depcheck                # Find unused dependencies
npx why express@4.17        # Why is this version installed?
Enter fullscreen mode Exit fullscreen mode

Workspaces (Monorepo Basics)

// Root package.json
{
  "name": "monorepo",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "build": "npm run build --workspaces-if-present",
    "test": "npm test --workspaces-if-present",
    "clean": "npm exec --workspaces -- rimraf dist node_modules"
  }
}

# Benefits:
# - Shared node_modules (saves disk space, faster installs)
# - Run scripts across all packages at once
# - Packages can depend on each other via workspace:*
# - Single npm install at root installs everything

# Add internal dependency:
# cd packages/utils
# npm install workspace:shared-types
# This creates a symlink! No need to publish.
Enter fullscreen mode Exit fullscreen mode

Useful npm Config Tricks

# Set default config
npm config set init-author-name "Your Name"
npm config set init-author-email "you@example.com"
npm config set init-license MIT
npm config set init-version 1.0.0

# Per-project config (.npmrc)
# .npmrc file in project root:
save-exact=true           # Always save exact versions (no ^)
package-lock=true         # Always generate lockfile
engine-strict=true        # Fail if Node version doesn't match engines

# Speed up installs
npm config set prefer-offline true
npm config set audit=false          # Skip audit during install (faster)
npm config set fund=false           # No funding message

# Registry configuration
npm config set registry https://registry.npmmirror.com  # Mirror (China)
# Or scoped registries:
# @scope:registry=https://private-registry.com
# //private-registry.com/:_authToken=xxx

# View current config
npm config list
npm config list -l         # Include defaults
Enter fullscreen mode Exit fullscreen mode

What's your favorite npm trick? What do you wish npm could do that it can't?

Follow @armorbreak for more practical developer guides.

Top comments (0)