Repository navigation
Fix yarn PnP detection ignoring nodeLinker (#975, #539) - #978
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A Yarn 2 to Yarn 4 migration that switched nodeLinker away from pnp keeps a stale .pnp.js, and apply and vendor refuse the project as Plug'n'Play (#975). A lock-only checkout of a real PnP project has no loader yet, so vendor wires it and every later re-run refuses (#539). Assisted-by: Claude Code:claude-opus-5-5
Every Plug'n'Play check looked only for a .pnp.* loader file. A Yarn 2 to Yarn 4 migration that switched nodeLinker to node-modules or pnpm keeps the old .pnp.js, which yarn ignores, so apply and vendor refused a project whose packages are in node_modules and hosted scans warned that nothing was scanned (#975). Read yarn's effective nodeLinker (YARN_NODE_LINKER, else the nearest .yarnrc.yml that sets it) and ignore a loader the linker disowns. Forward vendoring also refuses a berry project configured for PnP (nodeLinker: pnp, or unset, berry's default) before its loader exists, so a lock-only checkout gets the same answer as an installed one instead of being wired once and refused on every re-run (#539). Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
|
[agent] Generated by Claude Code |
|
BugBot review Generated by Claude Code |
A pnpm node-linker=pnp tree writes the same .pnp.cjs as yarn. When an ancestor .yarnrc.yml or YARN_NODE_LINKER set a non-pnp yarn linker, the loader counted as stale and vendor wired the pnpm project instead of refusing it. Decide the pnpm case on any loader first, then apply the yarn linker check. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 59eac7e. Configure here.
|
[agent] Ready for review at
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #975
Fixes #539
Root cause
Every "is this a yarn Plug'n'Play project" decision keyed on whether a
.pnp.*loader file exists, never on the linker yarn is configured to use. That covered the agent crawler (crawlers/pkg_managers.rs::detect_npm_pkg_manager), the vendored flavor probe andvlt_routes(vendor/npm_flavor.rs) and the in-memory lock-inventory view (vendor/lock_inventory/view.rs)..pnp.js, and socket-patch refuses them as Plug'n'Play: agent and vendored exit 1, hosted warns "npm dependencies were NOT scanned" (regression since 3.3.0) #975: a Yarn 2 → Yarn 4 migration that switched tonodeLinker: node-modulesorpnpmkeeps the old.pnp.js, and yarn 4 never deletes it. socket-patch refused the project as PnP even though packages are innode_modules/. Agent and vendored runs exited 1, and hosted printed a false "npm dependencies were NOT scanned".nodeLinker: pnp, or unset, which is berry's default) has no loader yet. Vendored mode wired it, then refused every re-run onceyarn installwrote.pnp.cjs.Fix
pkg_managers::yarn_node_linkerreturns the linker yarn resolves:YARN_NODE_LINKERfirst, else the nearest.yarnrc.ymlat or above the project that setsnodeLinker.live_pnp_marker/live_pnp_marker_withcount a loader file only while that linker ispnpor unset. Every formerPNP_MARKERScheck goes through them: the crawler, the vendored probe,vlt_routesand the memory view. That view reads the snapshot's root.yarnrc.yml.npm_flavor::detect_vendorable_npm_flavoris the forward-vendoring probe. It also refuses (vendor_yarn_berry_unsupported) a yarn berry project whose configured linker ispnp, explicit or by default, before any loader exists.vendor_npm_any,preflight_packagesandlock_text_refusalsuse it, so takeovers refuse before they revert anything. Read-only paths keep the plain probe:vendor --check,--revert, VEX and the hosted lock inventory.file:wiring does work under PnP) would be a separate enhancement for a maintainer to decide. The compatibility doc now says PnP follows the configured linker.utils::digest. Main'sproduction_digests_go_through_the_helpersguard is red without it. The port is a no-op once Route Gradle digests through utils::digest #878 lands.Per-issue tests (red on main → green here)
e2e_safety_yarn_pnp::stale_pnp_loader_under_non_pnp_linker_applies(node-modules and pnpm linkers)yarn_pnp_unsupportedin_process_vendor::berry_stale_pnp_loader_under_non_pnp_linker_vendorspkg_managers::stale_pnp_loader_under_non_pnp_linker_is_not_pnp,npm_flavor::stale_pnp_loader_under_non_pnp_linker_is_not_refused,view::memory_flavor_probe_follows_the_disk_decision_tablein_process_vendor::berry_lock_only_pnp_project_refused_up_front(explicitpnp, rc withoutnodeLinker, no rc; lock-only and after install; nothing written)npm_flavor::vendorable_probe_refuses_berry_configured_for_pnpnpm_flavor::pnpm_pnp_layout_refuses_under_a_non_pnp_yarn_linkernode-linker=pnptreepnp_loader_under_explicit_pnp_linker_still_refuses,pkg_managers::pnp_loader_counts_under_pnp_or_unset_linker,yarn_node_linker_follows_yarn_precedenceLocal evidence
cargo fmt --all -- --check: clean.cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features: everything passes except 12 write-failure tests acrosscovgap_commands_vendor,in_process_redirect,repairand the core lib. Those tests need a write to be denied, and they cannot deny one when run as root, which the sandbox is (uid 0). They are unrelated to this diff, and CI (non-root) does not fail them.scripts/yarn-berry-vex-matrix.sh 4.12.0: all four suites pass (e2e_redirect_yarn_berry_build15,e2e_vendor_yarn_berry_build18,e2e_yarn4_pnpm_linker_build19,e2e_yarn4_workspaces_build17). In the legacy-refusal suite the yarn 3 cells pass; the two yarn 2.4.3 cells could not fetch yarn 2.4.3 through this sandbox's network policy. CI runs them.🤖 Generated with Claude Code
https://claude.ai/code/session_01GK9XtmEPwV6tL1bvzkj9ks
Note
Medium Risk
Changes which Yarn Berry projects are treated as PnP across apply, vendor, scan, and hosted flows—misread linker config could patch or wire projects that should be refused, or vice versa.
Overview
Yarn Plug'n'Play is now decided from the configured
nodeLinker(andYARN_NODE_LINKER), not merely from a leftover.pnp.*file on disk. Stale loaders after migrating tonode-modulesorpnpmno longer block apply, vendor, or npm-family scans; live PnP projects configured withpnp(or berry’s default) are still refused where documented, including lock-only checkouts beforeyarn installcreates a loader.Core behavior is centralized in
yarn_node_linker/live_pnp_marker(crawler, vendored probes, lock inventory) anddetect_vendorable_npm_flavorfor forward vendoring paths. This diff adds e2e and in-process vendor tests for #975 and #539, plus repo-widerustfmt/ import-order tweaks across CLI commands and tests (no functional edits in those formatting-only hunks).Reviewed by Cursor Bugbot for commit 59eac7e. Configure here.
Generated by Claude Code