DEV Community

Alex Chen
Alex Chen

Posted on

npm Scripts and package.json Mastery (2026)

npm Scripts and package.json Mastery (2026)

Your package.json is the control center of your project. Here's how to use it effectively.

package.json Anatomy

{
  "name": "my-awesome-project",
  "version": "1.2.3",                    // Semantic versioning (MAJOR.MINOR.PATCH)
  "description": "A brief description",
  "type": "module",                      // ES Modules (use import/export!)

  "main": "./dist/index.js",            // Entry point for require()
  "types": "./dist/index.d.ts",         // TypeScript type definitions
  "bin": {                              // CLI commands
    "mytool": "./cli.js"
  },

  "files": [                            // What gets published to npm
    "dist/",
    "README.md",
    "LICENSE"
  ],

  "engines": {
    "node": ">=18.0.0",                // Minimum Node.js version
    "npm": ">=9.0.0"
  },

  "scripts": {                          // THE most important section!
    "dev": "node --watch server.js",
    "build": "tsc",
    "start": "node dist/index.js",
    "test": "jest",
    "lint": 'eslint src/',
    "format": "prettier --write \"src/**/*.{js,ts}\"',
    "clean": "rm -rf dist"
  },

  "dependencies": {                     // Production dependencies
    "express": "^4.18.0",
    "lodash": "~4.17.21"               // ~ = patch updates only
  },

  "devDependencies": {                  // Development-only tools
    "typescript": "^5.0.0",
    "jest": "^29.0.0",
    "eslint": "^8.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Script Essentials

{
  "scripts": {
    "start": "node server.js",

    "dev": "nodemon server.js",

    "build": "npm run clean && tsc && npm run copy-assets",

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

    "lint": "eslint . --ext .ts,.js",
    "lint:fix": "eslint . --ext .ts,.js --fix",
    "format": "prettier --write \"src/**/*.{ts,js,json,md}\"",
    "check": "npm run lint && npm run format && npm run test",

    "prepare": "husky install",          // Runs automatically on npm install

    "deploy": "npm run build && rsync -avz dist/ user@server:/app/"
  }
}
Enter fullscreen mode Exit fullscreen mode
# Running scripts:
npm run dev          # Runs "dev" script
npm test             # Special: doesn't need "run" for these: start, test, stop, restart
npm run build        # Runs "build" script

# Chaining scripts:
# Use && for sequential (stops on failure):
npm run clean && npm run build && npm run test

# Use & for parallel (both run simultaneously):
npm run lint & npm run test

# Use ; for unconditional sequential (continues even if one fails):
npm run clean ; npm run build

# Passing arguments:
npm run test -- --grep "auth"           # Everything after -- goes to the command
npm run build -- --production            # Pass flags to underlying command

# Pre/Post hooks (automatic!):
npm run prebuild   # Automatically runs before "build"
npm run postbuild  # Automatically runs after "build" succeeds
# Available hooks: pre/post + script name
# Also: prepublishOnly, preinstall, postinstall
Enter fullscreen mode Exit fullscreen mode

Environment Variables in Scripts

{
  "scripts": {
    "dev": "NODE_ENV=development node server.js",
    "prod": "NODE_ENV=production node dist/index.js",

    "dev:windows": "set NODE_ENV=development&& node server.js",

    "db:migrate": "node scripts/migrate.js",
    "db:seed": "NODE_ENV=test node scripts/seed.js",

    "start:staging": "dotenv -e .env.staging -- npm start",
    "start:prod": "dotenv -e .env.production -- npm start"
  }
}
Enter fullscreen mode Exit fullscreen mode
# Better approach: use a .env file and dotenv-cli
npm install -D dotenv-cli

# .env file:
PORT=3000
DATABASE_URL=postgresql://localhost/myapp
NODE_ENV=development

# In scripts:
"dev": "dotenv -- node --watch server.js"

# Or use cross-env for Windows/Mac/Linux compatibility:
npm install -D cross-env
"build": "cross-env NODE_ENV=production webpack --mode production"
Enter fullscreen mode Exit fullscreen mode

Useful npm Configurations

# Per-project config (.npmrc):
# .npmrc
save-exact=true              # Save exact versions (^1.2.3 not ^1.2.x)
engine-strict=true           # Fail if Node version doesn't match engines field
package-lock=true            # Always generate lockfile
fund=false                   # Disable funding message
audit=true                  # Run audit on install

# Global config (~/.npmrc):
prefix=/usr/local           # Where global packages install
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
//company-registry.com/:_authToken=${COMPANY_NPM_TOKEN}
Enter fullscreen mode Exit fullscreen mode

Common Workflows

# Starting a new project:
npm init -y                  # Create default package.json
npm install express          # Add dependency
npm install -D typescript    # Add dev dependency
npm install -g nodemon       # Install globally

# Updating dependencies:
npm outdated                 # Check what's outdated
npm update                   # Update per package.json rules
npx npm-check-updates -u    # Update package.json to latest versions
npm install                 # Reinstall from updated package.json

# Auditing:
npm audit                   # Check for vulnerabilities
npm audit fix               # Auto-fix where possible
npm audit --json            # Machine-readable output (for CI)

# Publishing your own package:
npm login                   # Authenticate
npm publish                 # Publish to registry
npm publish --dry-run       # Test without actually publishing
npm deprecate <pkg> "msg"  # Deprecate a published version

# Managing global packages:
npm list -g --depth=0       # List global top-level packages
npm uninstall -g <pkg>      # Remove global package
Enter fullscreen mode Exit fullscreen mode

Pro Tips

{
  "config": {
    "port": "3000",
    "logLevel": "info"
  },
  "scripts": {
    // Access config via npm_package_config_port
    "start": "node server.js $npm_package_config_port",

    // Use npx to run CLI tools without installing:
    "format:check": "prettier --check .",
    "bundle:analyze": "webpack-bundle-analyzer dist/stats.json",
    "type-check": "tsc --noEmit",

    // Conditional scripts:
    "test:ci": "CI=true jest --ci --coverage --reporters=default --reporters=jest-junit",

    // Monorepo support (workspaces):
    "workspaces": ["packages/*"],
    "start": "npm run -w packages/server start",
    "test": "npm run -ws test"  # Run test in all workspaces
  }
}

Enter fullscreen mode Exit fullscreen mode
# npx — run without installing permanently:
npx create-react-app my-app     # Run once, don't keep installed
npx @tailwindcss/cli init       # Try a tool before committing
npx serve ./dist                # Quick local server
npx http-server ./dist -p 8080  # Another quick server option
npx json-server db.json         # Mock REST API from JSON file
npx degit user/repo template    # Scaffold like git clone but fresh

# npm link for local development:
cd my-library
npm link                        # Link this package globally

cd my-app
npm link my-library             # Use local version of library
# Changes to my-library are immediately visible in my-app!

# Version management:
npm version patch               # 1.0.0 → 1.0.1 (+ git commit + tag!)
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
Enter fullscreen mode Exit fullscreen mode

What's your favorite npm trick? What's in your package.json that you can't live without?

Follow @armorbreak for more practical developer guides.

Top comments (0)