Skip to content

pacquet: complete pnpmfile hooks (filterLog, pnpm:hook log channel, pnpmfile flags, multiple pnpmfiles, updateConfig, finders) #12118

Description

@zkochan

Background

PR #12044 adds Tier 4 pnpmfile hooks to pacquet (the Rust port). It landed the two hooks that affect resolution output and the lockfile, plus the infrastructure to make them efficient:

  • readPackage — wired into resolution with pnpm's requirePnpmfile contract (defaults the four dependency fields to {} before the hook; validates the returned manifest; a throwing/invalid hook or a hook that returns nothing aborts the install with PNPMFILE_FAIL).
  • afterAllResolved — wired into the lockfile write; the lockfile round-trips through serde_json::Value (workspace preserve_order) so hook-added keys survive to disk; a throwing hook aborts the install.
  • preResolution — wired (one-shot node invocation with an info/warn logger).
  • Persistent Node worker — replaces the fresh node -e process per hook call with one long-lived worker per pnpmfile, multiplexing concurrent readPackage calls over a newline-delimited JSON protocol and forwarding each context.log(...) back to the call's HookContext.

Ported pnpm tests: readPackage validation/normalization + install-level pinning/failure, afterAllResolved mutation/failure, worker concurrency + log forwarding.

This issue tracks the pnpmfile-hook behavior that pnpm has but pacquet does not yet implement, so the remaining upstream hook tests can be ported.

Remaining work

1. pnpm:hook log channel

The worker already forwards context.log(...) to the call's HookContext, but at the readPackage call site (resolve_dependency_tree) and the afterAllResolved call site the log closure is a no-op. Wire a LogEvent::Hook reporter variant (payload parity with pnpm's hookLogger: name: 'pnpm:hook', prefix, from, hook, message) and thread a reporter-backed log closure into the hook call sites. Note: resolve_dependency_tree is not currently generic over Reporter, so this needs plumbing.

Unblocks porting: "pnpmfile: pass log function to readPackage hook", "...of global and local pnpmfile", "pnpmfile: run afterAllResolved hook", "...run async afterAllResolved hook".

2. filterLog wired into the reporter

filter_log is implemented in the worker but never called in the install path. Reporter::emit is a stateless, synchronous static method, so running an async worker hook from it requires the reporter to carry the configured hook(s) — a reporter redesign. Until then, log filtering is a no-op.

Unblocks porting: "filterLog hook filters peer dependency warning", "filterLog hook combines with the global hook".

3. pnpmfile-location CLI flags: --ignore-pnpmfile, --pnpmfile, --global-pnpmfile

All three are pure CLI flags (in upstream's excludedPnpmKeys), so they thread from clap into the install path rather than living in Config. Each needs a new field on the Install struct (~68 construction sites across the crate, mostly tests) plus clap threading across install/add/remove/update, and the install path must skip / redirect finder::load_pnpmfile accordingly.

Unblocks porting: "readPackage hook from custom location", "...from global pnpmfile", "...from global pnpmfile and local pnpmfile" (+ async), "ignore .pnpmfile.cjs when --ignore-pnpmfile is used" (+ during update).

4. Multiple / global pnpmfiles

pnpm loads a global pnpmfile plus the default plus any --pnpmfile entries, and combines array hooks (readPackage hooks run in sequence; filterLog hooks are AND-ed). pacquet currently discovers a single default pnpmfile. Needs the finder to return an ordered list and the bridge/worker to run them in sequence.

Unblocks porting: "readPackage hooks array", "loading multiple pnpmfiles", "filterLog hook combines with the global hook", "pass readPackage with shared lockfile" (workspace).

5. pnpmfile checksum

pnpm records a pnpmfile checksum in workspace state and re-runs hooks when it changes (adding or changing pnpmfile should change pnpmfileChecksum and module structure). pacquet's workspace-state has no pnpmfile-checksum field, so changing a pnpmfile does not invalidate an up-to-date install.

6. updateConfig hook

Not implemented. pnpm runs updateConfig(config) from the pnpmfile and errors if it returns undefined ("updateConfig throws an error if it returns undefined").

7. Finders / resolvers / fetchers

Not implemented (finders, resolvers, fetchers exports). pnpm merges finders across pnpmfiles and errors on duplicate finder names ("requireHooks merges all the finders", "...two finders with the same name").

Notes

  • Each item should be its own PR with the corresponding upstream tests ported (see pnpm/test/install/hooks.ts, pnpm/test/hooks.ts, installing/deps-installer/test/install/hooks.ts, hooks/pnpmfile/test/index.ts).
  • Suggested order: (1) pnpm:hook channel and (3) --ignore-pnpmfile are the most self-contained; (2) filterLog and (4) multiple pnpmfiles are the larger architectural pieces.

Written by an agent (Claude Code, claude-opus-4-8).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions