DEV Community

Alex Chen
Alex Chen

Posted on

npm Scripts: Unlocking the Full Power of package.json

npm Scripts: Unlocking the Full Power of package.json

Most developers only use npm start and npm test. Here's what you're missing.

The Basics

{
  "scripts": {
    "start": "node server.js",
    "test": "jest",
    "build": "tsc"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run with: npm run start or npm start (for start/test only)

Key insight: npm scripts automatically add node_modules/.bin to your PATH. So you can run locally-installed CLI tools directly!

Common Useful Scripts

{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "build": "tsc && vite build",
    "test": "jest --coverage",
    "test:watch": "jest --watch",
    "lint": 'eslint "src/**/*.{ts,js}"',
    "lint:fix": 'eslint "src/**/*.{ts,js}" --fix',
    "format": "prettier --write \"src/**/*.{ts,js,json,md}\"",
    "clean": "rm -rf dist build .turbo",
    "typecheck": "tsc --noEmit",
    "prepare": "husky install"
  }
}
Enter fullscreen mode Exit fullscreen mode

Pre/Post Hooks

{
  "scripts": {
    "prebuild": "npm run lint && npm run typecheck",
    "build": "tsc",
    "postbuild": "npm run clean:old && echo 'Build complete!'",

    "pretest": "npm run db:migrate:latest",
    "test": "jest",
    "posttest": "npx codecov"
  }
}
Enter fullscreen mode Exit fullscreen mode

When you run npm run build, it automatically runs:

  1. prebuild (lint + typecheck)
  2. build (compile)
  3. postbuild (cleanup)

Same for npm test: pretest → test → posttest

Variables & Arguments

# Pass arguments to scripts
npm run test -- --grep "auth"     # -- passes everything after to the script
npm run dev -- --port 8080        # Pass port to nodemon
npm run build -- --watch          # Pass watch flag to tsc

# Use variables in scripts
# package.json:
{
  "scripts": {
    "serve": "serve -l $PORT dist/"
  }
}
# Terminal:
PORT=3000 npm run serve
Enter fullscreen mode Exit fullscreen mode

Chaining Commands

{
  "scripts": {
    // Sequential (run one after another, stop on failure)
    "setup": "npm install && npm run build && npm run migrate",

    // Parallel (run all at once)
    "validate": "npm run lint & npm run typecheck & wait",

    // Regardless of previous result
    "cleanup": "npm run clean; rm -rf logs/*",

    // Conditional
    "deploy": "npm run test && npm run build && npm run push:prod"
  }
}
Enter fullscreen mode Exit fullscreen mode

Cross-Platform Scripts

// ❌ Won't work on Windows
"clean": "rm -rf dist/"

// ✅ Use cross-env for env vars
// npm install -D cross-env
"build": "cross-env NODE_ENV=production webpack"

// ✅ Use rimraf for file operations
// npm install -D rimraf
"clean": "rimraf dist/"

// ✅ Use npm-run-all for parallel execution
// npm install -D npm-run-all
"validate": "npm-run-all lint typecheck test"

// ✅ Use shx for shell commands (cross-platform)
// npm install -D shx
"copy": "shx cp src/index.html dist/"
Enter fullscreen mode Exit fullscreen mode

Script Organization (Large Projects)

{
  "scripts": {
    // Development
    "dev": "nodemon server.js",
    "dev:debug": "node --inspect server.js",

    // Building
    "build": "tsc",
    "build:watch": "tsc --watch",
    "build:production": "tsc && vite build",

    // Quality
    "lint": 'eslint src/',
    "lint:fix": 'eslint src/ --fix',
    "typecheck": "tsc --noEmit",
    "format": "prettier --write src/",
    "format:check": "prettier --check src/",

    // Testing
    "test": "jest",
    "test:e2e": "playwright test",
    "test:coverage": "jest --coverage",
    "test:watch": "jest --watch",

    // Database
    "db:migrate": "prisma migrate dev",
    "db:migrate:prod": "prisma migrate deploy",
    "db:seed": "prisma db seed",
    "db:studio": "prisma studio",

    // Deployment
    "deploy": "npm run build && npm run deploy:cf",
    "deploy:cf": "wrangler deploy",
    "deploy:vercel": "vercel --prod",

    // Utility
    "clean": "rimraf dist build .cache",
    "deps:check": "npm outdated",
    "deps:update": "npm update && npm audit fix"
  }
}
Enter fullscreen mode Exit fullscreen mode

Pro Tips

1. Script Alias with npx

# Run any npm package without installing globally
npx create-react-app my-app
npx tsc --version        # Check TypeScript version
npx prettier . --check   # Check formatting
npx http-server ./dist   # Quick local server
npx cowsay "Hello!"      # Fun stuff too
Enter fullscreen mode Exit fullscreen mode

2. Workspaces (Monorepo Scripts)

// Root package.json
{
  "workspaces": ["packages/*"],
  "scripts": {
    "build": "npm run build --workspaces-if-present",
    "test": "npm test --workspaces-if-present",
    "dev": "concurrently \"npm run dev:*\""
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Lifecycle Scripts (Automatic)

{
  "scripts": {
    "prepare": "husky",       // Runs after npm install
    "prepublishOnly": "npm run build && npm test",  // Before npm publish
    "preinstall": "node check-node-version.js",      // Before npm install
    "postinstall": "patch-package"                   // After npm install
  }
}
Enter fullscreen mode Exit fullscreen mode
When What Runs
npm install preinstall → install → postinstall
npm publish prepublishOnly → prepublish → publish
npm run xxx prexxx → xxx → postxxx

4. Debug Your Scripts

# See what command actually runs
npm run build --verbose

# Dry run (don't execute)
npm run build --dry-run

# Print each command as it runs
# In script: set -x (bash) or use npm config
npm config set script-shell bash
Enter fullscreen mode Exit fullscreen mode

What's your favorite npm script trick?

Follow @armorbreak for more Node.js content.

Top comments (0)