TechLead
Lesson 12 of 20
5 min read
DevTools & Productivity

npm Advanced

Master npm workspaces, scripts, package publishing, dependency management, and security auditing

Beyond npm install

Most developers use npm install and npm run daily but rarely explore npm's advanced capabilities. npm is a powerful package manager with features for workspaces, security auditing, script orchestration, package publishing, and dependency optimization. Mastering these features makes you significantly more productive in managing JavaScript projects.

npm Scripts Power Features

// package.json - Advanced script patterns
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",

    "lint": "eslint . --ext .ts,.tsx",
    "lint:fix": "eslint . --ext .ts,.tsx --fix",
    "format": "prettier --write '**/*.{ts,tsx,json,md}'",
    "format:check": "prettier --check '**/*.{ts,tsx,json,md}'",
    "typecheck": "tsc --noEmit",

    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:ui": "vitest --ui",

    "db:generate": "prisma generate",
    "db:push": "prisma db push",
    "db:migrate": "prisma migrate dev",
    "db:seed": "prisma db seed",
    "db:studio": "prisma studio",

    "precommit": "npm run lint && npm run typecheck && npm run test",
    "prepare": "husky",

    "clean": "rm -rf .next node_modules/.cache",
    "clean:all": "rm -rf .next node_modules dist coverage",

    "analyze": "ANALYZE=true next build",
    "check:all": "npm run lint && npm run typecheck && npm run test && npm run build"
  }
}

Script Lifecycle Hooks

  • pre<script>: Runs before the named script. prebuild runs before build.
  • post<script>: Runs after the named script. postinstall runs after npm install.
  • prepare: Runs after npm install and before npm publish. Used for build steps and Husky setup.
  • prepublishOnly: Runs before the package is published. Good for final checks and builds.

Dependency Management

# View outdated packages
npm outdated                     # Show outdated packages with current/wanted/latest
npm outdated --long              # Include package type and homepage

# Update packages
npm update                       # Update all packages within semver range
npm update lodash                # Update specific package
npx npm-check-updates            # Show what would change if you updated everything
npx npm-check-updates -u         # Update package.json to latest versions
npx npm-check-updates -i         # Interactive mode: choose which to update

# Inspect packages
npm ls                           # Show dependency tree
npm ls react                     # Find which packages depend on react
npm ls --depth=0                 # Show only top-level dependencies
npm explain lodash               # Show why lodash is installed (dependency chain)
npm why lodash                   # Same as explain

# View package info
npm info react                   # Full package metadata
npm info react version           # Just the latest version
npm info react versions          # All published versions
npm view react dependencies      # See a package's dependencies

# Clean install (CI-friendly)
npm ci                           # Install exactly from package-lock.json
                                 # Faster and more reliable than npm install

npm Workspaces

npm workspaces (v7+) enable managing multiple packages within a single repository (monorepo). Shared dependencies are hoisted to the root, and packages can reference each other without publishing.

// Root package.json
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}

// packages/shared/package.json
{
  "name": "@myorg/shared",
  "version": "1.0.0",
  "main": "src/index.ts",
  "types": "src/index.ts"
}

// apps/web/package.json
{
  "name": "@myorg/web",
  "dependencies": {
    "@myorg/shared": "*"
  }
}
# Workspace commands
npm install                          # Install all workspace dependencies
npm run build -w packages/shared     # Run script in specific workspace
npm run build --workspaces           # Run script in all workspaces
npm run test --workspaces --if-present  # Run if script exists

# Add dependency to specific workspace
npm install zod -w packages/shared
npm install -D vitest -w apps/web

# Run command across all workspaces
npm exec --workspaces -- tsc --noEmit

Security Auditing

# Run security audit
npm audit                    # Check for known vulnerabilities
npm audit --json             # Machine-readable output
npm audit fix                # Auto-fix compatible vulnerabilities
npm audit fix --force        # Fix even with breaking changes (use with caution)

# Audit specific production dependencies only
npm audit --omit=dev

# Check for specific advisories
npm audit --audit-level=high  # Only show high and critical

# Use Snyk for deeper analysis
npx snyk test
npx snyk monitor             # Continuous monitoring

.npmrc Configuration

# .npmrc - Project-level npm configuration
# Place in project root (committed to Git)

# Use exact versions by default (no ^ or ~)
save-exact=true

# Set default registry (for private packages)
registry=https://registry.npmjs.org/
@myorg:registry=https://npm.pkg.github.com/

# Engine strict mode (fail if Node version doesn't match)
engine-strict=true

# Automatically install peer dependencies
legacy-peer-deps=false

# Reduce output noise
fund=false
audit=true
loglevel=warn

Publishing Packages

// package.json for a publishable package
{
  "name": "@myorg/my-library",
  "version": "1.0.0",
  "description": "A useful library",
  "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"],
  "scripts": {
    "build": "tsup src/index.ts --format cjs,esm --dts",
    "prepublishOnly": "npm run build && npm test"
  },
  "publishConfig": {
    "access": "public"
  },
  "engines": {
    "node": ">=18"
  }
}
# Publishing workflow
npm login                        # Authenticate with npm registry
npm version patch                # Bump 1.0.0 -> 1.0.1 (also creates git tag)
npm version minor                # Bump 1.0.0 -> 1.1.0
npm version major                # Bump 1.0.0 -> 2.0.0
npm publish                      # Publish to registry
npm publish --dry-run             # Preview what would be published
npm pack                          # Create tarball to inspect contents
npm deprecate @myorg/pkg "Use @myorg/new-pkg instead"  # Deprecate

npm Best Practices

  • Always commit package-lock.json: It ensures reproducible builds across all environments.
  • Use npm ci in CI: It is faster and ensures exact versions from the lockfile.
  • Run npm audit regularly: Set up automated security scanning in your CI pipeline.
  • Use engines field: Specify required Node.js version to prevent compatibility issues.
  • Prefer exact versions: Set save-exact=true in .npmrc for predictable dependency resolution.

Continue Learning