npm scripts and lifecycle hooks

npm · Essentials · 12 min

Scripts are the shared, documented command interface of every JavaScript project.

The scripts field maps names to shell commands. npm run <name> executes them with node_modules/.bin prepended to PATH, so locally installed tools run without global installs. npm test, npm start, npm stop and npm restart have shortcuts. npm run with no name lists all scripts.

Details worth knowing:

  • Arguments: everything after -- is passed to the script: npm run test -- --watch.
  • Pre and post hooks: prebuild runs before build, postbuild after, automatically.
  • Lifecycle scripts run during other commands: prepare (after a local install and before publish), prepublishOnly, preinstall/install/postinstall (when a package is installed as a dependency).
  • Environment: scripts see npm_package_name, npm_package_version, npm_lifecycle_event and more.
  • Scripts run in sh on Unix and cmd.exe on Windows by default, so avoid shell-specific syntax (or set script-shell) in cross-platform projects.
  • npm run <script> --workspaces runs it in every workspace; --if-present skips packages without it.

Node 22+ offers node --run <script>, which starts faster but skips pre/post hooks and npm's environment variables.

Keep scripts short; move complex logic into a scripts/*.js file run with node.

Example

{
  "name": "shop-api",
  "version": "1.4.0",
  "type": "module",
  "scripts": {
    "dev": "node --watch src/server.js",
    "lint": "eslint .",
    "test": "node --test",
    "pretest": "npm run lint",
    "build": "tsc -p tsconfig.build.json",
    "postbuild": "node scripts/copy-assets.js",
    "version:print": "node -e \"console.log(process.env.npm_package_version)\""
  }
}

// npm test                      -> runs pretest (lint) then test
// npm run test -- --test-only   -> passes --test-only to node --test
// npm run version:print         -> 1.4.0
// node --run build              -> faster, but skips prebuild/postbuild

Remember

  • npm run adds node_modules/.bin to PATH
  • Pass arguments after --
  • pre<name>/post<name> hooks run automatically
  • node --run is faster but skips hooks

Watch out for

  • Using Bash-only syntax in scripts that Windows contributors must run.

Check yourself

  1. What runs when you type `npm test` and `pretest` is defined?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.npmjs.com/cli/v11/using-npm/scripts