Skip to content

feat: add preunpack lifecycle script that runs before installation - #8972

Closed
DukeDeSouth wants to merge 1 commit into
npm:latestfrom
DukeDeSouth:fix/preunpack-lifecycle-2660
Closed

feat: add preunpack lifecycle script that runs before installation#8972
DukeDeSouth wants to merge 1 commit into
npm:latestfrom
DukeDeSouth:fix/preunpack-lifecycle-2660

Conversation

@DukeDeSouth

Copy link
Copy Markdown

Summary

Since npm v7, the preinstall script runs after dependencies are installed — making it functionally identical to postinstall and eliminating the ability to run scripts before installation begins. This has been an open bug for 5 years (#2660, 142 reactions, 72 comments, Priority 1 label).

This PR implements RFC #403 by adding a new preunpack lifecycle script that runs BEFOREarb.reify() — i.e., before any dependencies are fetched or installed.

Problem

Current npm install flow:
arb.reify() ← ALL deps installed here
preinstall ← runs AFTER deps (useless for pre-install tasks)
install
postinstall
prepare

Users cannot:

  • Authenticate to private registries before fetching deps (AWS CodeArtifact, Azure, GCP)
  • Run bootstrap/setup scripts (gRPC workspace generation, environment validation)
  • Enforce package manager restrictions
  • Block malicious dependency code from executing

Solution

New npm install flow:
preunpack ← NEW: runs BEFORE any installation
arb.reify() ← deps installed
preinstall ← unchanged (backwards compatible)
install
postinstall
prepare

Behavior

Scenariopreunpack runs?
npm install (no args, local)Yes
npm ciYes
npm install <pkg>No
npm install -gNo
--ignore-scriptsNo
Script exits non-zeroInstall aborted
No preunpack in package.jsonNo-op (zero impact)

Example usage

{
"scripts": {
"preunpack": "aws codeartifact login --tool npm --repository my-repo --domain my-domain"
}
}

Changes

FileChange
lib/commands/install.jsAdd preunpack execution before Arborist require
lib/commands/ci.jsAdd preunpack execution before arb.reify()
lib/commands/run.jsAdd preunpack to known lifecycle scripts list
docs/.../scripts.mdDocument preunpack event and update operation order
test/.../install.js3 new tests + update lifecycle order assertion
test/.../ci.js2 new tests + update lifecycle order assertion

+239 lines, -2 lines. Non-breaking change.

Prior Art

  • RFC #403 — Accepted RFC for preunpack (discussed in npm meeting 2021-07-21)
  • PR #2713 — Attempted to move preinstall before reify (closed: breaking change)
  • PR #5264 — Proposed prefetch script (closed: no response)
  • PR #6888 — Proposed preinstallOnly (closed: abandoned)
  • Internal branch owlstronaut/feat-preunpack (2025-06-19) — Started implementation but never merged

Test Plan

  • npm install — preunpack runs first in lifecycle order (9/9 tests pass)
  • npm ci — preunpack runs first in lifecycle order (12/12 tests pass)
  • npm install — preunpack runs before arb.reify()
  • npm ci — preunpack runs before arb.reify()
  • npm install <pkg> — preunpack does NOT run
  • --ignore-scripts — preunpack does NOT run
  • All existing tests pass (0 regressions)

Fixes#2660
Implements npm/rfcs#403

Made with Cursor

Adds a new `preunpack` lifecycle script that runs BEFORE dependencies are
installed. This provides a hook for setup tasks that must happen before any
packages are fetched, such as authenticating to private registries or
validating environment prerequisites.
Since npm v7, the `preinstall` script runs AFTER dependencies are installed
(alongside `install` and `postinstall`), eliminating the ability to run
scripts before the installation begins. This has been a pain point for
5 years (142 reactions, 72 comments on npm#2660).
The `preunpack` script:
- Runs on local `npm install` (without arguments) and `npm ci`
- Does NOT run for specific package installs, global installs, or
when --ignore-scripts is set
- Aborts installation if the script exits with a non-zero code
- Is listed as a known lifecycle script in `npm run` output
Implements: npm/rfcs#403Fixes: npm#2660
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Preinstall script runs after installing dependencies

2 participants

@DukeDeSouth@Tanker187