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"
}
}
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/"
}
}
# 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
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"
}
}
# 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"
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}
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
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
}
}
# 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
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)