Files
pnpm/pnpm11/installing
Zoltan Kochan 1a3406d82d fix(cli): accept a --no-<flag> negation for every boolean flag (#12812)
pnpm parses its CLI with nopt, which auto-accepts a `--no-<name>`
negation for every option typed as Boolean. Pacquet's flags are plain
`#[clap(long)] bool`s that only accept the positive spelling, so a
forwarded `--no-frozen-lockfile` (or `--no-lockfile-only`, `--no-dry-run`,
and so on) aborted the parser with "unexpected argument" -- which broke
`pnpm install --no-frozen-lockfile` when the v12 binary ran the
pnpm.io benchmarks.

Add a generic augmentation layer instead of a per-flag paired negation:
`with_boolean_negations` walks the built clap Command tree and, for every
boolean flag that lacks a `--no-` sibling, adds a hidden negation that
`overrides_with` the positive flag. Last-one-wins semantics reproduce
nopt exactly (`--no-foo` leaves the default `false`, `--foo --no-foo`
resolves to `false`). Explicit hand-written negations are detected and
left untouched; global flags get a global negation so the override
reference propagates into subcommands. Because the negations are real
clap args, the grammar stays intact -- trailing pass-through args
(`run`, `exec`, `dlx`) and value-taking flags are unaffected, and
genuinely unknown flags still error.

Give the config-OR-merged booleans real force-off semantics. For
`trust-lockfile`, `offline`, `prefer-offline`, `frozen-store`, and
`ignore-scripts`, pacquet resolved the effective value as
`config_value || cli_flag`, so it could never turn a yaml `true` back
off from the CLI. Left to the generic negation alone, `--no-<flag>`
would parse but silently no-op against a config `true` -- a footgun for
the security-sensitive `trustLockfile`, where a repo-controlled
`pnpm-workspace.yaml` could keep verification disabled past a user's
explicit `--no-trust-lockfile`. Each now exposes an explicit `--no-`
flag, resolved via the shared
`resolve_bool_override(force_on, force_off, config)`: force-on wins,
force-off wins over a config `true`, an unset pair falls through to
config -- matching pnpm, where a CLI boolean overrides the
workspace/`.npmrc` value in either direction.

Wire every explicit `--flag` / `--no-flag` pair (the five above plus the
pre-existing `prefer-frozen-lockfile`) with mutual `overrides_with`
rather than `conflicts_with`, so both spellings in one argv resolve
last-one-wins instead of aborting the parser -- pnpm forwards raw argv
tokens, so a valid `--offline --no-offline` must not fail at the pacquet
boundary. The override collapses the pair to the last-specified, so
`resolve_bool_override` still sees at most one side set.

Also refresh the stale rationale in runPacquet.ts for why pnpm strips
the user's frozen-lockfile flags before delegating: pnpm resolves the
frozen-lockfile setting itself and encodes it in the mode it hands
pacquet (a resolving install, or a frozen materialization with an
injected `--frozen-lockfile`), so forwarding the user's own token would
contradict that choice via pacquet's last-wins negation override.
2026-07-05 10:58:33 +02:00
..
2026-07-04 21:05:03 +02:00
2026-07-04 21:05:03 +02:00
2026-07-04 21:05:03 +02:00
2026-07-04 21:05:03 +02:00