workspace_node_tools/
paths.rs1#![allow(clippy::all)]
2
3use super::utils::strip_trailing_newline;
7use execute::Execute;
8use std::{
9 env,
10 path::{Path, PathBuf},
11 process::{Command, Stdio},
12};
13
14pub fn get_project_root_path(root: Option<PathBuf>) -> Option<String> {
16 let env_dir = match root {
17 Some(dir) => Ok(dir),
18 None => env::current_dir(),
19 };
20
21 let current_dir = match env_dir {
22 Ok(dir) => dir,
23 _ => PathBuf::from("./"),
24 };
25 let current_path = current_dir.as_path();
26
27 let git_root_dir = walk_reverse_dir(¤t_path);
28
29 let project_root = match git_root_dir {
30 Some(current) => current,
31 None => {
32 let search_root = get_git_root_dir(¤t_path);
33 search_root.unwrap_or(current_path.to_str().unwrap().to_string())
34 }
35 };
36
37 let canonic_path = &std::fs::canonicalize(Path::new(&project_root)).unwrap();
38 let root = canonic_path.as_path().display().to_string();
39
40 Some(root)
41}
42
43fn get_git_root_dir(dir: &Path) -> Option<String> {
45 let mut command = Command::new("git");
46 command.arg("rev-parse").arg("--show-toplevel");
47
48 command.current_dir(dir);
49
50 command.stdout(Stdio::piped());
51 command.stderr(Stdio::piped());
52
53 let output = command.execute_output().unwrap();
54
55 if output.status.success() {
56 let output = String::from_utf8(output.stdout).unwrap();
57 return Some(strip_trailing_newline(&output));
58 }
59
60 None
61}
62
63fn walk_reverse_dir(path: &Path) -> Option<String> {
65 let current_path = path.to_path_buf();
66 let map_files = vec![
67 ("package-lock.json", "npm"),
68 ("npm-shrinkwrap.json", "npm"),
69 ("yarn.lock", "yarn"),
70 ("pnpm-lock.yaml", "pnpm"),
71 ("bun.lockb", "bun"),
72 ];
73
74 for (file, _) in map_files.iter() {
75 let lock_file = current_path.join(file);
76
77 if lock_file.exists() {
78 return Some(current_path.to_str().unwrap().to_string());
79 }
80 }
81
82 if let Some(parent) = path.parent() {
83 return walk_reverse_dir(parent);
84 }
85
86 None
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 use crate::manager::PackageManager;
94 use crate::utils::create_test_monorepo;
95 use std::fs::{remove_dir_all, rename};
96 use std::path::Path;
97
98 fn git_dir_rename(from: &Path, to: &Path) {
99 rename(from, to).expect("Rename dir");
100 }
101
102 #[test]
103 fn npm_root_project() -> Result<(), Box<dyn std::error::Error>> {
104 let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?;
105 let git_home = monorepo_dir.join(".git");
106 let no_git = monorepo_dir.join(".no_git");
107
108 git_dir_rename(&git_home, &no_git);
109
110 let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
111
112 assert_eq!(
113 project_root,
114 Some(monorepo_dir.to_str().unwrap().to_string())
115 );
116
117 remove_dir_all(&monorepo_dir)?;
118 Ok(())
119 }
120
121 #[test]
122 fn yarn_root_project() -> Result<(), Box<dyn std::error::Error>> {
123 let ref monorepo_dir = create_test_monorepo(&PackageManager::Yarn)?;
124 let git_home = monorepo_dir.join(".git");
125 let no_git = monorepo_dir.join(".no_git");
126
127 git_dir_rename(&git_home, &no_git);
128
129 let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
130
131 assert_eq!(
132 project_root,
133 Some(monorepo_dir.to_str().unwrap().to_string())
134 );
135
136 remove_dir_all(&monorepo_dir)?;
137 Ok(())
138 }
139
140 #[test]
141 fn pnpm_root_project() -> Result<(), Box<dyn std::error::Error>> {
142 let ref monorepo_dir = create_test_monorepo(&PackageManager::Pnpm)?;
143 let git_home = monorepo_dir.join(".git");
144 let no_git = monorepo_dir.join(".no_git");
145
146 git_dir_rename(&git_home, &no_git);
147
148 let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
149
150 assert_eq!(
151 project_root,
152 Some(monorepo_dir.to_str().unwrap().to_string())
153 );
154
155 remove_dir_all(&monorepo_dir)?;
156 Ok(())
157 }
158
159 #[test]
160 fn bun_root_project() -> Result<(), Box<dyn std::error::Error>> {
161 let ref monorepo_dir = create_test_monorepo(&PackageManager::Bun)?;
162 let git_home = monorepo_dir.join(".git");
163 let no_git = monorepo_dir.join(".no_git");
164
165 git_dir_rename(&git_home, &no_git);
166
167 let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
168
169 assert_eq!(
170 project_root,
171 Some(monorepo_dir.to_str().unwrap().to_string())
172 );
173
174 remove_dir_all(&monorepo_dir)?;
175 Ok(())
176 }
177
178 #[test]
179 fn git_root_project() -> Result<(), Box<dyn std::error::Error>> {
180 let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?;
181 let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
182
183 assert_eq!(project_root.is_some(), true);
184 remove_dir_all(&monorepo_dir)?;
185 Ok(())
186 }
187}