use std::path::{Path, PathBuf};
use serde_json::Value;
use skif_core::FileSystem;
const SECTIONS: [&str; 4] = [
"dependencies",
"devDependencies",
"optionalDependencies",
"peerDependencies",
];
pub(crate) fn is_repository_root(fs: &dyn FileSystem, dir: &Path) -> bool {
let kind = fs.kind(&dir.join(".git"));
kind.is_dir || kind.is_file
}
pub(crate) fn repository_root(fs: &dyn FileSystem, dir: &Path) -> Option<PathBuf> {
dir.ancestors()
.find(|candidate| is_repository_root(fs, candidate))
.map(Path::to_path_buf)
}
pub(crate) fn external_manifests(fs: &dyn FileSystem, root: &Path) -> Vec<(PathBuf, Value)> {
external_manifests_up_to(fs, root, repository_root(fs, root).as_deref())
}
pub(crate) fn external_manifests_up_to(
fs: &dyn FileSystem,
root: &Path,
git_root: Option<&Path>,
) -> Vec<(PathBuf, Value)> {
if git_root == Some(root) {
return Vec::new();
}
let mut manifests = Vec::new();
for dir in root.ancestors().skip(1) {
if let Some(manifest) = read_manifest(fs, dir) {
manifests.push((dir.to_path_buf(), manifest));
}
if git_root == Some(dir) {
break;
}
}
manifests
}
pub(crate) fn read_manifest(fs: &dyn FileSystem, dir: &Path) -> Option<Value> {
fs.read_to_string(&dir.join("package.json"))
.ok()
.and_then(|text| serde_json::from_str::<Value>(&text).ok())
}
pub(crate) fn listed_names(manifest: &Value) -> impl Iterator<Item = &str> {
SECTIONS
.into_iter()
.filter_map(|section| manifest.get(section).and_then(Value::as_object))
.flat_map(|listed| listed.keys().map(String::as_str))
}
pub(crate) fn external_dependencies(fs: &dyn FileSystem, root: &Path, git_root: Option<&Path>) -> Vec<String> {
let mut names: Vec<String> = external_manifests_up_to(fs, root, git_root)
.iter()
.flat_map(|(_, manifest)| listed_names(manifest).map(str::to_owned))
.collect();
names.sort();
names.dedup();
names
}