diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index c97ed89e2e744..ea2acd9b79df1 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 + .values() + .map(RelativeUnixPathBuf::new) + .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..6b2bc981ebeb5 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,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().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") } } }