changepacks_node/
finder.rs1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use changepacks_core::{Project, ProjectFinder};
4use std::{
5 collections::HashMap,
6 path::{Path, PathBuf},
7};
8use tokio::fs::read_to_string;
9
10use crate::{package::NodePackage, workspace::NodeWorkspace};
11
12#[derive(Debug)]
13pub struct NodeProjectFinder {
14 projects: HashMap<PathBuf, Project>,
15 project_files: Vec<&'static str>,
16}
17
18impl Default for NodeProjectFinder {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24impl NodeProjectFinder {
25 pub fn new() -> Self {
26 Self {
27 projects: HashMap::new(),
28 project_files: vec!["package.json"],
29 }
30 }
31}
32
33#[async_trait]
34impl ProjectFinder for NodeProjectFinder {
35 fn projects(&self) -> Vec<&Project> {
36 self.projects.values().collect::<Vec<_>>()
37 }
38 fn projects_mut(&mut self) -> Vec<&mut Project> {
39 self.projects.values_mut().collect::<Vec<_>>()
40 }
41
42 fn project_files(&self) -> &[&str] {
43 &self.project_files
44 }
45
46 async fn visit(&mut self, path: &Path, relative_path: &Path) -> Result<()> {
47 if path.is_file()
49 && self.project_files().contains(
50 &path
51 .file_name()
52 .context(format!("File name not found - {}", path.display()))?
53 .to_str()
54 .context(format!("File name not found - {}", path.display()))?,
55 )
56 {
57 if self.projects.contains_key(path) {
58 return Ok(());
59 }
60 let package_json = read_to_string(path).await?;
62 let package_json: serde_json::Value = serde_json::from_str(&package_json)?;
63 if package_json.get("workspaces").is_some()
65 || path
66 .parent()
67 .context(format!("Parent not found - {}", path.display()))?
68 .join("pnpm-workspace.yaml")
69 .is_file()
70 {
71 let version = package_json["version"].as_str().map(|v| v.to_string());
72 let name = package_json["name"].as_str().map(|v| v.to_string());
73 self.projects.insert(
74 path.to_path_buf(),
75 Project::Workspace(Box::new(NodeWorkspace::new(
76 name,
77 version,
78 path.to_path_buf(),
79 relative_path.to_path_buf(),
80 ))),
81 );
82 } else {
83 let version = package_json["version"]
84 .as_str()
85 .context(format!("Version not found - {}", path.display()))?
86 .to_string();
87 let name = package_json["name"]
88 .as_str()
89 .context(format!("Name not found - {}", path.display()))?
90 .to_string();
91
92 self.projects.insert(
93 path.to_path_buf(),
94 Project::Package(Box::new(NodePackage::new(
95 name,
96 version,
97 path.to_path_buf(),
98 relative_path.to_path_buf(),
99 ))),
100 );
101 }
102 }
103 Ok(())
104 }
105}