From b1785c7a01b835b5e0bb6889af3dbfea60c9370a Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Thu, 21 May 2026 15:14:50 -0600 Subject: [PATCH 1/2] fix: Preserve nested Bun workspace dependency versions --- crates/turborepo-lockfiles/src/bun/mod.rs | 113 ++++++++++++++++-- .../fixtures/bun-v1-issue-12921/bun.lock | 39 ++++++ .../fixtures/bun-v1-issue-12921/meta.json | 7 ++ .../fixtures/bun-v1-issue-12921/package.json | 9 ++ .../packages/a/package.json | 12 ++ .../packages/b/package.json | 11 ++ .../fixtures/bun-v1-issue-12921/turbo.json | 6 + 7 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 lockfile-tests/fixtures/bun-v1-issue-12921/bun.lock create mode 100644 lockfile-tests/fixtures/bun-v1-issue-12921/meta.json create mode 100644 lockfile-tests/fixtures/bun-v1-issue-12921/package.json create mode 100644 lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json create mode 100644 lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json create mode 100644 lockfile-tests/fixtures/bun-v1-issue-12921/turbo.json diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index a49282a90b42f..0e4a1196821ec 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -728,6 +728,37 @@ impl Lockfile for BunLockfile { } } + let workspace_deps: HashMap> = workspace_packages + .iter() + .filter(|ws_path| !ws_path.is_empty()) + .filter_map(|ws_path| { + let workspace_entry = self.data.workspaces.get(ws_path.as_str())?; + let mut deps = BTreeMap::new(); + if let Some(d) = &workspace_entry.dependencies { + deps.extend(d.clone()); + } + if let Some(dd) = &workspace_entry.dev_dependencies { + deps.extend(dd.clone()); + } + if let Some(od) = &workspace_entry.optional_dependencies { + deps.extend(od.clone()); + } + if let Some(pd) = &workspace_entry.peer_dependencies { + deps.extend(pd.clone()); + } + (!deps.is_empty()).then(|| (ws_path.clone(), deps)) + }) + .collect(); + + if !workspace_deps.is_empty() { + let workspace_closures = crate::all_transitive_closures(self, workspace_deps, true)?; + packages_with_workspaces.extend( + workspace_closures + .values() + .flat_map(|closure| closure.iter().map(|package| package.key.clone())), + ); + } + for ws_path in workspace_packages { if ws_path.is_empty() { continue; @@ -1457,12 +1488,18 @@ impl BunLockfile { if let Some(dealiased_key) = parsed_key.dealias() { let dealiased_str = dealiased_key.to_string(); - // Check if dealiasing would conflict with an existing workspace mapping + // Check if dealiasing would conflict with an existing package. + // If the top-level key points at a different ident, keep the + // nested key so both versions remain addressable. let would_conflict = if let Some(existing_entry) = self.data.packages.get(&dealiased_str) { let ident = PackageIdent::parse(&existing_entry.ident); + let current_entry = self.data.packages.get(key); ident.is_workspace() + || current_entry + .map(|entry| entry.ident != existing_entry.ident) + .unwrap_or(false) } else { false }; @@ -1495,14 +1532,13 @@ impl BunLockfile { if let Some(dealiased_key) = parsed_key.dealias() { let dealiased_str = dealiased_key.to_string(); - // Check if dealiasing would conflict with an existing workspace - // mapping + // Check if dealiasing would conflict with an existing package. + // Different idents must keep distinct keys. if let Some(existing_entry) = self.data.packages.get(&dealiased_str) { let ident = PackageIdent::parse(&existing_entry.ident); - if ident.is_workspace() { - // This would conflict with a workspace mapping - keep full - // key + if ident.is_workspace() || entry.ident != existing_entry.ident { + // This would conflict with another package - keep full key. key.clone() } else { // No conflict - safe to dealias @@ -1812,6 +1848,12 @@ impl BunLockfile { }) .collect(); + let pruned_workspace_names: HashSet = pruned_data + .workspaces + .values() + .map(|workspace| workspace.name.clone()) + .collect(); + pruned_data.packages.retain(|key, entry| { if entry.ident.contains("@workspace:") { return true; @@ -1820,7 +1862,8 @@ impl BunLockfile { let parsed = PackageKey::parse(key); if let Some(parent) = parsed.parent() { reachable_idents.contains(&entry.ident) - && reachable_lockfile_keys.contains(&parent) + && (reachable_lockfile_keys.contains(&parent) + || pruned_workspace_names.contains(&parent)) } else { reachable_idents.contains(&entry.ident) } @@ -4129,4 +4172,60 @@ mod test { "alias-specific nested child should be restored when its sibling remains" ); } + + #[test] + fn test_subgraph_preserves_nested_workspace_dependency_version() { + let contents = serde_json::to_string(&json!({ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "root" + }, + "packages/a": { + "name": "a", + "version": "0.0.1", + "dependencies": { + "b": "*" + }, + "devDependencies": { + "b": "workspace:*", + "is-number": "^7.0.0" + } + }, + "packages/b": { + "name": "b", + "version": "0.0.1", + "devDependencies": { + "is-number": "6.0.0" + }, + "peerDependencies": { + "is-number": "6.0.0" + } + } + }, + "packages": { + "a": ["a@workspace:packages/a"], + "b": ["b@workspace:packages/b"], + "is-number": ["is-number@7.0.0", "", {}, "sha512-7"], + "b/is-number": ["is-number@6.0.0", "", {}, "sha512-6"] + } + })) + .unwrap(); + + let lockfile = BunLockfile::from_str(&contents).unwrap(); + let subgraph = ::subgraph( + &lockfile, + &["packages/a".into(), "packages/b".into()], + &["is-number@7.0.0".into()], + ) + .unwrap(); + + let encoded = subgraph.encode().unwrap(); + let encoded_str = String::from_utf8(encoded).unwrap(); + let pruned = BunLockfile::from_str(&encoded_str).unwrap(); + + assert_eq!(pruned.data.packages["is-number"].ident, "is-number@7.0.0"); + assert_eq!(pruned.data.packages["b/is-number"].ident, "is-number@6.0.0"); + } } diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/bun.lock b/lockfile-tests/fixtures/bun-v1-issue-12921/bun.lock new file mode 100644 index 0000000000000..31a6e8a0b9c4c --- /dev/null +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/bun.lock @@ -0,0 +1,39 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "@foretagsplatsen/frontend", + }, + "packages/a": { + "name": "a", + "version": "0.0.1", + "dependencies": { + "b": "*", + }, + "devDependencies": { + "b": "workspace:*", + "is-number": "^7.0.0", + }, + }, + "packages/b": { + "name": "b", + "version": "0.0.1", + "devDependencies": { + "is-number": "6.0.0", + }, + "peerDependencies": { + "is-number": "6.0.0", + }, + }, + }, + "packages": { + "a": ["a@workspace:packages/a"], + + "b": ["b@workspace:packages/b"], + + "is-number": ["is-number@7.0.0", "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "b/is-number": ["is-number@6.0.0", "https://registry.npmjs.org/is-number/-/is-number-6.0.0.tgz", {}, "sha512-Wu1VHeILBK8KAWJUAiSZQX94GmOE45Rg6/538fKwiloUu21KncEkYGPqob2oSZ5mUT73vLGrHQjKw3KMPwfDzg=="], + } +} diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/meta.json b/lockfile-tests/fixtures/bun-v1-issue-12921/meta.json new file mode 100644 index 0000000000000..86c72f9663f4a --- /dev/null +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/meta.json @@ -0,0 +1,7 @@ +{ + "packageManager": "bun", + "packageManagerVersion": "bun@1.3.14", + "lockfileName": "bun.lock", + "docker": true, + "pruneTargets": ["a"] +} diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/package.json b/lockfile-tests/fixtures/bun-v1-issue-12921/package.json new file mode 100644 index 0000000000000..ffd5bd14f68af --- /dev/null +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/package.json @@ -0,0 +1,9 @@ +{ + "name": "my-root", + "private": true, + "workspaces": [ + "packages/a", + "packages/b" + ], + "packageManager": "bun@1.3.14" +} diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json new file mode 100644 index 0000000000000..80360fa21a8b9 --- /dev/null +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json @@ -0,0 +1,12 @@ +{ + "name": "a", + "private": true, + "version": "0.0.1", + "dependencies": { + "b": "*" + }, + "devDependencies": { + "b": "workspace:*", + "is-number": "^7.0.0" + } +} diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json new file mode 100644 index 0000000000000..d3030609b5d03 --- /dev/null +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json @@ -0,0 +1,11 @@ +{ + "name": "b", + "version": "0.0.1", + "peerDependencies": { + "is-number": "6.0.0" + }, + "dependencies": {}, + "devDependencies": { + "is-number": "6.0.0" + } +} diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/turbo.json b/lockfile-tests/fixtures/bun-v1-issue-12921/turbo.json new file mode 100644 index 0000000000000..a383ddeb487c9 --- /dev/null +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/turbo.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": {} + } +} From b08605a0f5b5e176899f16a498d5dd1c9deb445f Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Thu, 21 May 2026 15:15:18 -0600 Subject: [PATCH 2/2] chore: Format Bun issue fixture --- .../fixtures/bun-v1-issue-12921/packages/a/package.json | 2 +- .../fixtures/bun-v1-issue-12921/packages/b/package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json index 80360fa21a8b9..8be53043ef136 100644 --- a/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/a/package.json @@ -1,7 +1,7 @@ { "name": "a", - "private": true, "version": "0.0.1", + "private": true, "dependencies": { "b": "*" }, diff --git a/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json index d3030609b5d03..0cc60e1ba8876 100644 --- a/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json +++ b/lockfile-tests/fixtures/bun-v1-issue-12921/packages/b/package.json @@ -1,11 +1,11 @@ { "name": "b", "version": "0.0.1", - "peerDependencies": { - "is-number": "6.0.0" - }, "dependencies": {}, "devDependencies": { "is-number": "6.0.0" + }, + "peerDependencies": { + "is-number": "6.0.0" } }