install: do not auto-install a transitive peer dependency on bun

Closed
RO
1mo ago
+239-2

Problem

  • bun add bun-plugin-tailwind installs the npm bun package into node_modules: ~80MB of binaries on linux, ~170MB on Windows (#39755). bun why bun shows it comes from peer bun-plugin-tailwind@0.1.2 (requires >=1.0.0).
  • Bun auto-installs non-optional peer dependencies, npm does the same with this package. The plugin declares a hard peer dep on bun, and nothing tells the installer that the running runtime already satisfies it.

Fix

  • Treat a remote package's peer dependency named bun as an optional peer. Both parse paths change in src/install/lockfile/Package.rs: the registry manifest path (from_npm) and the generic package.json path, gated on !is_main && !is_workspace.
  • An optional peer is never auto-installed, but the existing hoister still binds it when the bun package is present through a real dependency edge.
  • A direct dependency on bun in the user's package.json (any kind, including a root peer dependency) still installs it.
  • Verified: test/cli/install/bun-install-registry.test.ts (new peer dependency on bun block, stock bun fails the first test). Also ran bun-lock.test.ts, isolated-install.test.ts, bun-update-transitive.test.ts, and the full registry suite.

Background

  • The installer records each dependency edge with Behavior flags. PEER | OPTIONAL means: bind to the package if something else installs it, otherwise leave the edge unresolved. --omit=peer and peerDependenciesMeta: { optional: true } already use this path, so lockfile round-trips and hoisting are covered by existing machinery.
  • Remote packages (registry, git, tarball, folder) parse with Features where is_main and is_workspace are false. The root and workspace package.json files parse with is_main or is_workspace set, so their own bun dependencies keep the old behavior.
  • New registry fixtures: bun@1.0.0/1.1.0 and peer-on-bun@1.0.0 (generator script create-bun-peer-packages.ts).
Notes
  • An existing bun.lock that already pins the bun peer keeps its resolution on reload: lockfile loads do not reparse manifests, so nothing is removed behind the user's back. A fresh resolve (no lockfile, or bun update) drops it.
  • If bun appears in both dependencies and peerDependencies of the same remote package, the duplicate-promotion loop in from_npm keeps the regular dependency, so it still installs.
  • The version range of the peer is not checked against the runtime version. The copy in node_modules never changes which runtime executes, so installing a newer bun package would not help anyway.
  • The saved bun.lock lists bun under optionalPeers for such packages, which keeps reinstalls from a saved lockfile stable (asserted in the test).
  • The 14 failures in bun-install.test.ts locally are network tests (bitbucket, gitlab, public tarball URLs) that cannot run in the sandbox, unchanged by this diff.

no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/install/bun-install-registry.test.ts