@@ -1361,9 +1361,36 @@ impl BunLockfile {
13611361 . flat_map ( |closure| closure. iter ( ) . map ( |p| p. key . clone ( ) ) )
13621362 . collect ( ) ;
13631363
1364+ // Also keep track of reachable lockfile keys for nested package detection
1365+ let reachable_lockfile_keys: HashSet < String > = recomputed_closures
1366+ . values ( )
1367+ . flat_map ( |closure| {
1368+ closure
1369+ . iter ( )
1370+ . filter_map ( |p| temp_lockfile. key_to_entry . get ( & p. key ) . cloned ( ) )
1371+ } )
1372+ . collect ( ) ;
1373+
13641374 // Remove unreachable packages
1365- pruned_data. packages . retain ( |_key, entry| {
1366- reachable_idents. contains ( & entry. ident ) || entry. ident . contains ( "@workspace:" )
1375+ pruned_data. packages . retain ( |key, entry| {
1376+ // Keep if the ident is reachable
1377+ if reachable_idents. contains ( & entry. ident )
1378+ || entry. ident . contains ( "@workspace:" )
1379+ {
1380+ return true ;
1381+ }
1382+
1383+ // Keep nested packages if their parent is reachable
1384+ // E.g., keep "@hatchet-dev/typescript-sdk/zod" if "@hatchet-dev/typescript-sdk"
1385+ // is reachable
1386+ if let Some ( slash_pos) = key. rfind ( '/' ) {
1387+ let parent_key = & key[ ..slash_pos] ;
1388+ if reachable_lockfile_keys. contains ( parent_key) {
1389+ return true ;
1390+ }
1391+ }
1392+
1393+ false
13671394 } ) ;
13681395 }
13691396 Err ( _e) => { }
@@ -1526,6 +1553,7 @@ mod test {
15261553 include_str ! ( "./snapshots/original-issue-11007-1.lock" ) ;
15271554 const PRUNE_ISSUE_11007_ORIGINAL_2 : & str =
15281555 include_str ! ( "./snapshots/original-issue-11007-2.lock" ) ;
1556+ const PRUNE_ISSUE_11074_ORIGINAL : & str = include_str ! ( "./snapshots/original-issue-11074.lock" ) ;
15291557
15301558 #[ test_case( "" , "turbo" , "^2.3.3" , "[email protected] " ; "root" ) ] 15311559 #[ test_case( "apps/docs" , "is-odd" , "3.0.1" , "[email protected] " ; "docs is odd" ) ] @@ -3004,6 +3032,28 @@ mod test {
30043032 }
30053033 }
30063034
3035+ // Add nested package entries (e.g., "parent/dep") for any packages we've
3036+ // collected This handles cases where a package has a nested version of
3037+ // a dependency (e.g., @hatchet-dev/typescript-sdk/zod for a different
3038+ // zod version)
3039+ let collected_packages: Vec < String > = packages. iter ( ) . cloned ( ) . collect ( ) ;
3040+ for pkg_ident in & collected_packages {
3041+ // Convert ident to lockfile key using key_to_entry
3042+ if let Some ( lockfile_key) = lockfile. key_to_entry . get ( pkg_ident) {
3043+ let prefix = format ! ( "{}/" , lockfile_key) ;
3044+ for nested_key in lockfile. data . packages . keys ( ) {
3045+ if nested_key. starts_with ( & prefix) {
3046+ // Add the nested key directly (it's a lockfile key, not an ident)
3047+ // We need to add both the key itself and its ident
3048+ packages. insert ( nested_key. clone ( ) ) ;
3049+ if let Some ( nested_entry) = lockfile. data . packages . get ( nested_key) {
3050+ packages. insert ( nested_entry. ident . clone ( ) ) ;
3051+ }
3052+ }
3053+ }
3054+ }
3055+ }
3056+
30073057 let packages: Vec < String > = packages. into_iter ( ) . collect ( ) ;
30083058
30093059 // Call internal subgraph method
@@ -3125,4 +3175,12 @@ mod test {
31253175 let pruned_str = String :: from_utf8 ( pruned. encode ( ) . unwrap ( ) ) . unwrap ( ) ;
31263176 insta:: assert_snapshot!( pruned_str) ;
31273177 }
3178+
3179+ #[ test]
3180+ fn test_prune_issue_11074 ( ) {
3181+ let lockfile = BunLockfile :: from_str ( PRUNE_ISSUE_11074_ORIGINAL ) . unwrap ( ) ;
3182+ let pruned = prune_for_workspace ( & lockfile, "apps/app-a" ) ;
3183+ let pruned_str = String :: from_utf8 ( pruned. encode ( ) . unwrap ( ) ) . unwrap ( ) ;
3184+ insta:: assert_snapshot!( pruned_str) ;
3185+ }
31283186}
0 commit comments