Skip to content

Commit

Permalink
chore: project root path detection feature
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelramos committed Nov 13, 2024
1 parent bc68d92 commit 7a5410c
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/standard/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod command;
pub mod error;
pub mod paths;
pub mod utils;
77 changes: 77 additions & 0 deletions crates/standard/src/paths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use super::command::execute;

use std::{
env,
ops::Deref,
path::{Path, PathBuf},
};

/// Get the project root path.
pub fn get_project_root_path(root: Option<PathBuf>) -> Option<PathBuf> {
let env_dir = match root {
Some(dir) => Ok(dir),
None => env::current_dir(),
};

let current_dir = match env_dir {
Ok(dir) => dir,
_ => PathBuf::from("./"),
};
let current_path = current_dir.as_path();
let git_root_dir = walk_reverse_dir(current_path);

let project_root = match git_root_dir {
Some(current) => current,
None => {
let search_root = get_git_root_dir(current_path);
search_root.unwrap_or(current_path.to_str().unwrap().to_string())
}
};

let canonic_path = &std::fs::canonicalize(Path::new(&project_root)).unwrap();

Some(canonic_path.deref().to_path_buf())
}

/// Get the git root directory.
fn get_git_root_dir(dir: &Path) -> Option<String> {
let top_level =
execute("git", dir, ["rev-parse", "--show-toplevel"], |stdout, _| Ok(stdout.to_string()));

match top_level {
Ok(output) => {
if output.is_empty() {
return None;
}

Some(output)
}
Err(_) => None,
}
}

/// Walk reverse directory to find the root project.
fn walk_reverse_dir(path: &Path) -> Option<String> {
let current_path = path.to_path_buf();
let map_files = vec![
("package-lock.json", "npm"),
("npm-shrinkwrap.json", "npm"),
("yarn.lock", "yarn"),
("pnpm-lock.yaml", "pnpm"),
("bun.lockb", "bun"),
];

for (file, _) in map_files {
let lock_file = current_path.join(file);

if lock_file.exists() {
return Some(current_path.to_str().unwrap().to_string());
}
}

if let Some(parent) = path.parent() {
return walk_reverse_dir(parent);
}

None
}
44 changes: 44 additions & 0 deletions crates/standard/tests/std_paths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#[cfg(test)]
mod paths_tests {
use std::{
env::temp_dir,
fs::{create_dir, remove_dir_all, set_permissions, File},
io::Write,
ops::Deref,
os::unix::fs::PermissionsExt,
path::PathBuf,
};

use ws_std::paths::get_project_root_path;

fn create_workspace() -> Result<PathBuf, std::io::Error> {
let temp_dir = temp_dir();
let monorepo_root_dir = temp_dir.join("monorepo-workspace");

if monorepo_root_dir.exists() {
remove_dir_all(&monorepo_root_dir)?;
}

create_dir(&monorepo_root_dir)?;

let mut readme_file = File::create(monorepo_root_dir.join("package.json").as_path())?;
readme_file.write_all(b"{}")?;

#[cfg(not(windows))]
set_permissions(&monorepo_root_dir, std::fs::Permissions::from_mode(0o777))?;

Ok(monorepo_root_dir)
}

#[test]
fn test_project_root_path() -> Result<(), std::io::Error> {
let root = &create_workspace()?;

let result = get_project_root_path(Some(root.deref().to_path_buf()));

remove_dir_all(root)?;

assert!(result.is_some());
Ok(())
}
}

0 comments on commit 7a5410c

Please sign in to comment.