Skip to content

Commit 2b4475e

Browse files
committed
Read package.json beside a root pnpm lock pair in hosted mode
The hosted pnpm planner picks which of a root pnpm-lock.yaml / shrinkwrap.yaml pair to rewrite from the pnpm major the project pins: node_modules/.modules.yaml, else package.json's packageManager. The hosted engine only read package.json beside an npm or yarn lock, so on a fresh clone that pins pnpm 1 or 2 in packageManager (no install record) hosted rewrote the stray pnpm-lock.yaml while pnpm installs from shrinkwrap.yaml. Vendored mode and discovery read the pin from disk and already chose shrinkwrap.yaml. Read the manifest as advisory input whenever both root pnpm locks are present, so all three agree. Addresses Bugbot 4500674838 on #2029. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aePC6sLFkqRM2XPWq4P
1 parent 6978424 commit 2b4475e

1 file changed

Lines changed: 43 additions & 2 deletions

File tree

‎crates/socket-patch-core/src/hosted/engine.rs‎

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,10 +621,17 @@ pub async fn read_candidate_files(
621621
// and yarn classic rewriters read it as advisory input only: no
622622
// rewriter edits it, so a link or an unreadable in-memory entry is left
623623
// out (the rewriters then keep their conservative reading) rather than
624-
// refused.
624+
// refused. Beside a root `pnpm-lock.yaml` / `shrinkwrap.yaml` pair its
625+
// `packageManager` pin decides which of the two pnpm reads
626+
// (`formats::pnpm::plan::planned_locks`), as vendored mode and
627+
// discovery read it, when no `node_modules/.modules.yaml` records the
628+
// installed major (a fresh clone).
625629
} else if candidates.iter().any(|c| c.dep.ecosystem == "npm")
626630
&& (NPM_LOCKS.iter().any(|lock| out.files.contains_key(*lock))
627-
|| out.files.contains_key("yarn.lock"))
631+
|| out.files.contains_key("yarn.lock")
632+
|| ROOT_PNPM_LOCKS
633+
.iter()
634+
.all(|lock| out.files.contains_key(*lock)))
628635
{
629636
let rel = crate::hosted::memory::select::NPM_MANIFEST_REL;
630637
if let Some(text) = read_advisory(view, unreadable, rel).await {
@@ -4332,6 +4339,40 @@ mod tests {
43324339
);
43334340
}
43344341

4342+
#[tokio::test]
4343+
async fn a_pnpm_lock_pair_reads_the_package_manager_pin() {
4344+
// pnpm 2 pinned in package.json, no install record: pnpm reads
4345+
// shrinkwrap.yaml, so the planner must see the pin to leave the
4346+
// stray pnpm-lock.yaml alone.
4347+
let tmp = tempfile::tempdir().unwrap();
4348+
let root = tmp.path();
4349+
let manifest = r#"{"name":"app","packageManager":"pnpm@2.25.7"}"#;
4350+
std::fs::write(root.join("package.json"), manifest).unwrap();
4351+
std::fs::write(root.join("pnpm-lock.yaml"), "lockfileVersion: 5.4\n").unwrap();
4352+
std::fs::write(
4353+
root.join("shrinkwrap.yaml"),
4354+
"shrinkwrapMinorVersion: 9\nshrinkwrapVersion: 3\n",
4355+
)
4356+
.unwrap();
4357+
let candidates = vec![left_pad_candidate()];
4358+
let read =
4359+
read_candidate_files(&ProjectView::Disk(root), &BTreeSet::new(), &candidates).await;
4360+
assert_eq!(
4361+
read.files.get("package.json").map(String::as_str),
4362+
Some(manifest)
4363+
);
4364+
assert_eq!(
4365+
crate::formats::pnpm::plan::planned_locks(&read.files),
4366+
vec!["shrinkwrap.yaml"]
4367+
);
4368+
4369+
// One pnpm lock alone governs whatever the pin: no manifest read.
4370+
std::fs::remove_file(root.join("shrinkwrap.yaml")).unwrap();
4371+
let read =
4372+
read_candidate_files(&ProjectView::Disk(root), &BTreeSet::new(), &candidates).await;
4373+
assert!(!read.files.contains_key("package.json"));
4374+
}
4375+
43354376
#[tokio::test]
43364377
async fn issue_490_the_root_manifest_overrides_reach_the_npm_rewriter() {
43374378
let redirected = |done: &Rewritten| {

0 commit comments

Comments
 (0)