Inside package-lock.json

npm · Expert · 12 min

Reading the lockfile directly answers questions no command does, and lets you review dependency changes in PRs.

Modern npm writes lockfileVersion 3. Its core is a packages object keyed by install location:

  • "" is the root project, repeating its dependency ranges.
  • "node_modules/express" is a hoisted package; "node_modules/a/node_modules/lodash" a nested one. Workspaces appear by folder ("packages/ui") plus a link: true entry under node_modules.

Each entry records:

  • version and resolved: the exact tarball URL. For private registries this reveals where packages come from.
  • integrity: a Subresource Integrity hash, normally sha512-.... npm verifies every downloaded tarball against it, so a tampered registry or mirror is detected.
  • dependencies, peerDependencies, optionalDependencies ranges as declared by that package, and engines, os, cpu, bin, license.
  • Flags: dev: true, optional: true, devOptional: true, peer: true, hasInstallScript: true (a quick way to list packages that run install scripts).

npm also keeps a hidden lockfile at node_modules/.package-lock.json, a cache of the actual tree that speeds up later installs.

The example is abridged. Reviewing lockfile diffs in PRs is real security work: unexpected resolved hosts, git URLs or new hasInstallScript packages deserve a closer look. Tools like lockfile-lint automate host and scheme checks.

Example

{
  "name": "shop-api",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": { "name": "shop-api", "dependencies": { "express": "^5.1.0" }, "devDependencies": { "esbuild": "^0.25.0" } },
    "node_modules/express": {
      "version": "5.1.0",
      "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
      "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
      "license": "MIT",
      "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0" },
      "engines": { "node": ">= 18" }
    },
    "node_modules/esbuild": {
      "version": "0.25.10",
      "dev": true,
      "hasInstallScript": true,
      "license": "MIT"
    }
  }
}

Remember

  • packages is keyed by install path, root is ""
  • integrity hashes are verified on every download
  • Flags like dev, optional and hasInstallScript classify entries
  • Review lockfile diffs: hosts, git URLs, install scripts

Watch out for

  • Approving PRs without looking at lockfile changes, where malicious URLs hide.

Check yourself

  1. What does npm do with the `integrity` field?

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

Official docs: docs.npmjs.com/cli/v11/configuring-npm/package-lock-json

Builds on

Unlocks

Nothing builds on this yet.