|
1 |
| -use std::{collections::HashMap, hash::Hash}; |
| 1 | +use std::{ |
| 2 | + collections::{HashMap, HashSet}, |
| 3 | + hash::Hash, |
| 4 | +}; |
2 | 5 |
|
3 | 6 | use napi::Error;
|
4 | 7 | use napi_derive::napi;
|
5 |
| -use turbopath::AbsoluteSystemPath; |
| 8 | +use turbopath::{AbsoluteSystemPath, AnchoredSystemPathBuf}; |
6 | 9 | use turborepo_repository::{
|
| 10 | + change_mapper::{ChangeMapper, PackageChanges}, |
7 | 11 | inference::RepoState as WorkspaceState,
|
8 |
| - package_graph::{PackageGraph, PackageNode, WorkspaceName}, |
| 12 | + package_graph::{PackageGraph, PackageNode, WorkspaceName, WorkspacePackage, ROOT_PKG_NAME}, |
9 | 13 | };
|
10 | 14 | mod internal;
|
11 | 15 |
|
@@ -131,4 +135,60 @@ impl Workspace {
|
131 | 135 |
|
132 | 136 | Ok(map)
|
133 | 137 | }
|
| 138 | + |
| 139 | + /// Given a set of "changed" files, returns a set of packages that are |
| 140 | + /// "affected" by the changes. The `files` argument is expected to be a list |
| 141 | + /// of strings relative to the monorepo root and use the current system's |
| 142 | + /// path separator. |
| 143 | + #[napi] |
| 144 | + pub async fn affected_packages(&self, files: Vec<String>) -> Result<Vec<Package>, Error> { |
| 145 | + let workspace_root = match AbsoluteSystemPath::new(&self.absolute_path) { |
| 146 | + Ok(path) => path, |
| 147 | + Err(e) => return Err(Error::from_reason(e.to_string())), |
| 148 | + }; |
| 149 | + |
| 150 | + let hash_set_of_paths: HashSet<AnchoredSystemPathBuf> = files |
| 151 | + .into_iter() |
| 152 | + .filter_map(|path| { |
| 153 | + let path_components = path.split(std::path::MAIN_SEPARATOR).collect::<Vec<&str>>(); |
| 154 | + let absolute_path = workspace_root.join_components(&path_components); |
| 155 | + workspace_root.anchor(&absolute_path).ok() |
| 156 | + }) |
| 157 | + .collect(); |
| 158 | + |
| 159 | + // Create a ChangeMapper with no custom global deps or ignore patterns |
| 160 | + let mapper = ChangeMapper::new(&self.graph, vec![], vec![]); |
| 161 | + let package_changes = match mapper.changed_packages(hash_set_of_paths, None) { |
| 162 | + Ok(changes) => changes, |
| 163 | + Err(e) => return Err(Error::from_reason(e.to_string())), |
| 164 | + }; |
| 165 | + |
| 166 | + let packages = match package_changes { |
| 167 | + PackageChanges::All => self |
| 168 | + .graph |
| 169 | + .workspaces() |
| 170 | + .map(|(name, info)| WorkspacePackage { |
| 171 | + name: name.to_owned(), |
| 172 | + path: info.package_path().to_owned(), |
| 173 | + }) |
| 174 | + .collect::<Vec<WorkspacePackage>>(), |
| 175 | + PackageChanges::Some(packages) => packages.into_iter().collect(), |
| 176 | + }; |
| 177 | + |
| 178 | + let mut serializable_packages: Vec<Package> = packages |
| 179 | + .into_iter() |
| 180 | + .filter(|p| match &p.name { |
| 181 | + WorkspaceName::Root => false, |
| 182 | + WorkspaceName::Other(name) => name != ROOT_PKG_NAME, |
| 183 | + }) |
| 184 | + .map(|p| { |
| 185 | + let package_path = workspace_root.resolve(&p.path); |
| 186 | + Package::new(p.name.to_string(), &workspace_root, &package_path) |
| 187 | + }) |
| 188 | + .collect(); |
| 189 | + |
| 190 | + serializable_packages.sort_by_key(|p| p.name.clone()); |
| 191 | + |
| 192 | + Ok(serializable_packages) |
| 193 | + } |
134 | 194 | }
|
0 commit comments