From a6463804f18ca76b402009d6e8dc4ef87b6e1e0d Mon Sep 17 00:00:00 2001 From: Youssef Gaber <1728215+Gabrola@users.noreply.github.com> Date: Fri, 31 Oct 2025 06:00:09 +0400 Subject: [PATCH 1/8] fix(prune): add bunfig.toml to list of copied files --- crates/turborepo-lib/src/commands/prune.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/turborepo-lib/src/commands/prune.rs b/crates/turborepo-lib/src/commands/prune.rs index dfdb9eadead82..6bca40c3bcdad 100644 --- a/crates/turborepo-lib/src/commands/prune.rs +++ b/crates/turborepo-lib/src/commands/prune.rs @@ -66,6 +66,10 @@ lazy_static! { RelativeUnixPath::new(".yarnrc.yml").unwrap(), Some(CopyDestination::Docker) ), + ( + RelativeUnixPath::new("bunfig.toml").unwrap(), + Some(CopyDestination::Docker) + ), ]; static ref ADDITIONAL_DIRECTORIES: Vec<(&'static RelativeUnixPath, Option)> = vec![ ( From 1754e3c428b82d64355c345e7b19c876cb22b07a Mon Sep 17 00:00:00 2001 From: Youssef Gaber <1728215+Gabrola@users.noreply.github.com> Date: Fri, 31 Oct 2025 06:40:03 +0400 Subject: [PATCH 2/8] fix(prune): correctly handle `patchedDependencies` for bun --- crates/turborepo-lockfiles/src/bun/mod.rs | 12 +++++ .../turborepo-repository/src/package_json.rs | 6 +++ .../src/package_manager/bun.rs | 52 +++++++++++++++++-- .../src/package_manager/mod.rs | 5 +- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index c97ed89e2e744..ab4b8c53353f7 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -100,6 +100,7 @@ use id::PossibleKeyIter; use itertools::Itertools as _; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; +use turbopath::RelativeUnixPathBuf; use turborepo_errors::ParseDiagnostic; use crate::Lockfile; @@ -497,6 +498,17 @@ impl Lockfile for BunLockfile { Ok(serde_json::to_vec_pretty(&self.data)?) } + fn patches(&self) -> Result, crate::Error> { + let mut patches = self + .data + .patched_dependencies + .iter() + .map(|(_, patch_file)| RelativeUnixPathBuf::new(patch_file)) + .collect::, turbopath::PathError>>()?; + patches.sort(); + Ok(patches) + } + fn global_change(&self, other: &dyn Lockfile) -> bool { let any_other = other as &dyn Any; // Downcast returns none if the concrete type doesn't match diff --git a/crates/turborepo-repository/src/package_json.rs b/crates/turborepo-repository/src/package_json.rs index 97010b12a2311..30e8a12249416 100644 --- a/crates/turborepo-repository/src/package_json.rs +++ b/crates/turborepo-repository/src/package_json.rs @@ -37,6 +37,8 @@ pub struct PackageJson { pub resolutions: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub pnpm: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub patched_dependencies: Option>, // Unstructured fields kept for round trip capabilities #[serde(flatten)] pub other: BTreeMap, @@ -64,6 +66,7 @@ pub struct RawPackageJson { pub scripts: BTreeMap>, pub resolutions: Option>, pub pnpm: Option, + pub patched_dependencies: Option>, // Unstructured fields kept for round trip capabilities #[deserializable(rest)] pub other: BTreeMap, @@ -135,6 +138,9 @@ impl From for PackageJson { .resolutions .map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect()), pnpm: raw.pnpm.map(|p| p.into()), + patched_dependencies: raw + .patched_dependencies + .map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect()), other: raw .other .into_iter() diff --git a/crates/turborepo-repository/src/package_manager/bun.rs b/crates/turborepo-repository/src/package_manager/bun.rs index f9d4c1e7b3429..9e57b9563ea44 100644 --- a/crates/turborepo-repository/src/package_manager/bun.rs +++ b/crates/turborepo-repository/src/package_manager/bun.rs @@ -1,6 +1,11 @@ -use turbopath::AbsoluteSystemPath; +use std::collections::HashSet; -use crate::package_manager::{Error, PackageManager}; +use turbopath::{AbsoluteSystemPath, RelativeUnixPath}; + +use crate::{ + package_json::PackageJson, + package_manager::{Error, PackageManager}, +}; pub const LOCKFILE: &str = "bun.lock"; pub const LOCKFILE_BINARY: &str = "bun.lockb"; @@ -40,13 +45,28 @@ impl Iterator for BunDetector<'_> { } } +pub(crate) fn prune_patches>( + package_json: &PackageJson, + patches: &[R], +) -> PackageJson { + let mut pruned_json = package_json.clone(); + let patches_set = patches.iter().map(|r| r.as_ref()).collect::>(); + + if let Some(existing_patches) = pruned_json.patched_dependencies.as_mut() { + existing_patches.retain(|_, patch_path| patches_set.contains(patch_path.as_ref())); + } + + pruned_json +} + #[cfg(test)] mod tests { - use std::assert_matches::assert_matches; + use std::{assert_matches::assert_matches, collections::BTreeMap}; use anyhow::Result; + use serde_json::json; use tempfile::tempdir; - use turbopath::AbsoluteSystemPathBuf; + use turbopath::{AbsoluteSystemPathBuf, RelativeUnixPathBuf}; use super::*; @@ -84,4 +104,28 @@ mod tests { assert_eq!(package_manager, PackageManager::Bun); Ok(()) } + + #[test] + fn test_patch_pruning() { + let package_json: PackageJson = PackageJson::from_value(json!({ + "name": "bun-patches", + "patchedDependencies": { + "foo@1.0.0": "patches/foo@1.0.0.patch", + "bar@1.2.3": "patches/bar@1.2.3.patch", + } + })) + .unwrap(); + let patches = vec![RelativeUnixPathBuf::new("patches/foo@1.0.0.patch").unwrap()]; + let pruned = prune_patches(&package_json, &patches); + assert_eq!( + pruned.patched_dependencies.as_ref(), + Some( + [("foo@1.0.0", "patches/foo@1.0.0.patch")] + .iter() + .map(|(k, v)| (k.to_string(), RelativeUnixPathBuf::new(*v).unwrap())) + .collect::>() + ) + .as_ref() + ); + } } diff --git a/crates/turborepo-repository/src/package_manager/mod.rs b/crates/turborepo-repository/src/package_manager/mod.rs index 6e7f82f039943..49653ed22b326 100644 --- a/crates/turborepo-repository/src/package_manager/mod.rs +++ b/crates/turborepo-repository/src/package_manager/mod.rs @@ -501,8 +501,9 @@ impl PackageManager { PackageManager::Pnpm9 | PackageManager::Pnpm6 | PackageManager::Pnpm => { pnpm::prune_patches(package_json, patches, repo_root) } - PackageManager::Yarn | PackageManager::Npm | PackageManager::Bun => { - unreachable!("bun, npm, and yarn 1 don't have a concept of patches") + PackageManager::Bun => bun::prune_patches(package_json, patches), + PackageManager::Yarn | PackageManager::Npm => { + unreachable!("npm and yarn 1 don't have a concept of patches") } } } From 884a163d3715e43d7bec29fe347611559c15aef8 Mon Sep 17 00:00:00 2001 From: Youssef Gaber <1728215+Gabrola@users.noreply.github.com> Date: Fri, 31 Oct 2025 06:40:03 +0400 Subject: [PATCH 3/8] fix(prune): correctly handle `patchedDependencies` for bun --- crates/turborepo-lockfiles/src/bun/mod.rs | 12 +++++ .../turborepo-repository/src/package_json.rs | 6 +++ .../src/package_manager/bun.rs | 52 +++++++++++++++++-- .../src/package_manager/mod.rs | 5 +- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index c97ed89e2e744..ab4b8c53353f7 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -100,6 +100,7 @@ use id::PossibleKeyIter; use itertools::Itertools as _; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; +use turbopath::RelativeUnixPathBuf; use turborepo_errors::ParseDiagnostic; use crate::Lockfile; @@ -497,6 +498,17 @@ impl Lockfile for BunLockfile { Ok(serde_json::to_vec_pretty(&self.data)?) } + fn patches(&self) -> Result, crate::Error> { + let mut patches = self + .data + .patched_dependencies + .iter() + .map(|(_, patch_file)| RelativeUnixPathBuf::new(patch_file)) + .collect::, turbopath::PathError>>()?; + patches.sort(); + Ok(patches) + } + fn global_change(&self, other: &dyn Lockfile) -> bool { let any_other = other as &dyn Any; // Downcast returns none if the concrete type doesn't match diff --git a/crates/turborepo-repository/src/package_json.rs b/crates/turborepo-repository/src/package_json.rs index 97010b12a2311..30e8a12249416 100644 --- a/crates/turborepo-repository/src/package_json.rs +++ b/crates/turborepo-repository/src/package_json.rs @@ -37,6 +37,8 @@ pub struct PackageJson { pub resolutions: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub pnpm: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub patched_dependencies: Option>, // Unstructured fields kept for round trip capabilities #[serde(flatten)] pub other: BTreeMap, @@ -64,6 +66,7 @@ pub struct RawPackageJson { pub scripts: BTreeMap>, pub resolutions: Option>, pub pnpm: Option, + pub patched_dependencies: Option>, // Unstructured fields kept for round trip capabilities #[deserializable(rest)] pub other: BTreeMap, @@ -135,6 +138,9 @@ impl From for PackageJson { .resolutions .map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect()), pnpm: raw.pnpm.map(|p| p.into()), + patched_dependencies: raw + .patched_dependencies + .map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect()), other: raw .other .into_iter() diff --git a/crates/turborepo-repository/src/package_manager/bun.rs b/crates/turborepo-repository/src/package_manager/bun.rs index f9d4c1e7b3429..9e57b9563ea44 100644 --- a/crates/turborepo-repository/src/package_manager/bun.rs +++ b/crates/turborepo-repository/src/package_manager/bun.rs @@ -1,6 +1,11 @@ -use turbopath::AbsoluteSystemPath; +use std::collections::HashSet; -use crate::package_manager::{Error, PackageManager}; +use turbopath::{AbsoluteSystemPath, RelativeUnixPath}; + +use crate::{ + package_json::PackageJson, + package_manager::{Error, PackageManager}, +}; pub const LOCKFILE: &str = "bun.lock"; pub const LOCKFILE_BINARY: &str = "bun.lockb"; @@ -40,13 +45,28 @@ impl Iterator for BunDetector<'_> { } } +pub(crate) fn prune_patches>( + package_json: &PackageJson, + patches: &[R], +) -> PackageJson { + let mut pruned_json = package_json.clone(); + let patches_set = patches.iter().map(|r| r.as_ref()).collect::>(); + + if let Some(existing_patches) = pruned_json.patched_dependencies.as_mut() { + existing_patches.retain(|_, patch_path| patches_set.contains(patch_path.as_ref())); + } + + pruned_json +} + #[cfg(test)] mod tests { - use std::assert_matches::assert_matches; + use std::{assert_matches::assert_matches, collections::BTreeMap}; use anyhow::Result; + use serde_json::json; use tempfile::tempdir; - use turbopath::AbsoluteSystemPathBuf; + use turbopath::{AbsoluteSystemPathBuf, RelativeUnixPathBuf}; use super::*; @@ -84,4 +104,28 @@ mod tests { assert_eq!(package_manager, PackageManager::Bun); Ok(()) } + + #[test] + fn test_patch_pruning() { + let package_json: PackageJson = PackageJson::from_value(json!({ + "name": "bun-patches", + "patchedDependencies": { + "foo@1.0.0": "patches/foo@1.0.0.patch", + "bar@1.2.3": "patches/bar@1.2.3.patch", + } + })) + .unwrap(); + let patches = vec![RelativeUnixPathBuf::new("patches/foo@1.0.0.patch").unwrap()]; + let pruned = prune_patches(&package_json, &patches); + assert_eq!( + pruned.patched_dependencies.as_ref(), + Some( + [("foo@1.0.0", "patches/foo@1.0.0.patch")] + .iter() + .map(|(k, v)| (k.to_string(), RelativeUnixPathBuf::new(*v).unwrap())) + .collect::>() + ) + .as_ref() + ); + } } diff --git a/crates/turborepo-repository/src/package_manager/mod.rs b/crates/turborepo-repository/src/package_manager/mod.rs index 6e7f82f039943..49653ed22b326 100644 --- a/crates/turborepo-repository/src/package_manager/mod.rs +++ b/crates/turborepo-repository/src/package_manager/mod.rs @@ -501,8 +501,9 @@ impl PackageManager { PackageManager::Pnpm9 | PackageManager::Pnpm6 | PackageManager::Pnpm => { pnpm::prune_patches(package_json, patches, repo_root) } - PackageManager::Yarn | PackageManager::Npm | PackageManager::Bun => { - unreachable!("bun, npm, and yarn 1 don't have a concept of patches") + PackageManager::Bun => bun::prune_patches(package_json, patches), + PackageManager::Yarn | PackageManager::Npm => { + unreachable!("npm and yarn 1 don't have a concept of patches") } } } From c0edcaaaa662643392ce2ca9a3dae6426211ce1e Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 5 Nov 2025 06:44:19 -0700 Subject: [PATCH 4/8] fix clippy --- crates/turborepo-lockfiles/src/bun/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index ab4b8c53353f7..bc877edbc616c 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -502,8 +502,8 @@ impl Lockfile for BunLockfile { let mut patches = self .data .patched_dependencies - .iter() - .map(|(_, patch_file)| RelativeUnixPathBuf::new(patch_file)) + .values() + .map(|patch_file| RelativeUnixPathBuf::new(patch_file)) .collect::, turbopath::PathError>>()?; patches.sort(); Ok(patches) From cfc62121e8cf83f11d92d9c100b01501e1f00d47 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 5 Nov 2025 06:46:28 -0700 Subject: [PATCH 5/8] Update crates/turborepo-lib/src/commands/prune.rs --- crates/turborepo-lib/src/commands/prune.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/turborepo-lib/src/commands/prune.rs b/crates/turborepo-lib/src/commands/prune.rs index 6bca40c3bcdad..dfdb9eadead82 100644 --- a/crates/turborepo-lib/src/commands/prune.rs +++ b/crates/turborepo-lib/src/commands/prune.rs @@ -66,10 +66,6 @@ lazy_static! { RelativeUnixPath::new(".yarnrc.yml").unwrap(), Some(CopyDestination::Docker) ), - ( - RelativeUnixPath::new("bunfig.toml").unwrap(), - Some(CopyDestination::Docker) - ), ]; static ref ADDITIONAL_DIRECTORIES: Vec<(&'static RelativeUnixPath, Option)> = vec![ ( From cf59004e452acdeb80d7831a15dc781feecf43fa Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 5 Nov 2025 06:56:59 -0700 Subject: [PATCH 6/8] clippy again --- crates/turborepo-lockfiles/src/bun/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index bc877edbc616c..ea2acd9b79df1 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -503,7 +503,7 @@ impl Lockfile for BunLockfile { .data .patched_dependencies .values() - .map(|patch_file| RelativeUnixPathBuf::new(patch_file)) + .map(RelativeUnixPathBuf::new) .collect::, turbopath::PathError>>()?; patches.sort(); Ok(patches) From cc5aeb66d184fa47863848774df75f344210fc81 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 5 Nov 2025 07:09:10 -0700 Subject: [PATCH 7/8] clippy again --- crates/turborepo-repository/src/package_json.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/turborepo-repository/src/package_json.rs b/crates/turborepo-repository/src/package_json.rs index 30e8a12249416..dc6b852d0f70c 100644 --- a/crates/turborepo-repository/src/package_json.rs +++ b/crates/turborepo-repository/src/package_json.rs @@ -140,7 +140,7 @@ impl From for PackageJson { pnpm: raw.pnpm.map(|p| p.into()), patched_dependencies: raw .patched_dependencies - .map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect()), + .map(|m| m.into_iter().map(|(k, v)| (k, v)).collect()), other: raw .other .into_iter() From 490814022a332a612b9801ef6555e047e4662805 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Wed, 5 Nov 2025 07:32:16 -0700 Subject: [PATCH 8/8] more clippy --- crates/turborepo-repository/src/package_json.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/turborepo-repository/src/package_json.rs b/crates/turborepo-repository/src/package_json.rs index dc6b852d0f70c..6b2bc981ebeb5 100644 --- a/crates/turborepo-repository/src/package_json.rs +++ b/crates/turborepo-repository/src/package_json.rs @@ -138,9 +138,7 @@ impl From for PackageJson { .resolutions .map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect()), pnpm: raw.pnpm.map(|p| p.into()), - patched_dependencies: raw - .patched_dependencies - .map(|m| m.into_iter().map(|(k, v)| (k, v)).collect()), + patched_dependencies: raw.patched_dependencies.map(|m| m.into_iter().collect()), other: raw .other .into_iter()