vika_cli/
formatter.rs

1use crate::error::Result;
2use std::path::Path;
3use std::process::Command;
4
5pub enum Formatter {
6    Prettier,
7    Biome,
8}
9
10pub struct FormatterManager;
11
12impl FormatterManager {
13    pub fn detect_formatter() -> Option<Formatter> {
14        // Check for prettier
15        if Self::has_prettier() {
16            return Some(Formatter::Prettier);
17        }
18
19        // Check for biome
20        if Self::has_biome() {
21            return Some(Formatter::Biome);
22        }
23
24        None
25    }
26
27    fn has_prettier() -> bool {
28        // Check for package.json with prettier
29        if Path::new("package.json").exists() {
30            if let Ok(content) = std::fs::read_to_string("package.json") {
31                if content.contains("prettier") {
32                    return true;
33                }
34            }
35        }
36
37        // Check for prettier config files
38        Path::new(".prettierrc").exists()
39            || Path::new(".prettierrc.json").exists()
40            || Path::new(".prettierrc.js").exists()
41            || Path::new("prettier.config.js").exists()
42    }
43
44    fn has_biome() -> bool {
45        // Check for biome.json
46        Path::new("biome.json").exists() || Path::new("biome.jsonc").exists()
47    }
48
49    pub fn format_files(files: &[std::path::PathBuf], formatter: Formatter) -> Result<()> {
50        match formatter {
51            Formatter::Prettier => Self::format_with_prettier(files),
52            Formatter::Biome => Self::format_with_biome(files),
53        }
54    }
55
56    fn format_with_prettier(files: &[std::path::PathBuf]) -> Result<()> {
57        let file_paths: Vec<String> = files
58            .iter()
59            .filter_map(|p| p.to_str().map(|s| s.to_string()))
60            .collect();
61
62        if file_paths.is_empty() {
63            return Ok(());
64        }
65
66        let output = Command::new("npx")
67            .arg("prettier")
68            .arg("--write")
69            .args(&file_paths)
70            .output();
71
72        match output {
73            Ok(_) => Ok(()),
74            Err(e) => {
75                // Silently fail if prettier is not available
76                eprintln!("Warning: Failed to run prettier: {}", e);
77                Ok(())
78            }
79        }
80    }
81
82    fn format_with_biome(files: &[std::path::PathBuf]) -> Result<()> {
83        let file_paths: Vec<String> = files
84            .iter()
85            .filter_map(|p| p.to_str().map(|s| s.to_string()))
86            .collect();
87
88        if file_paths.is_empty() {
89            return Ok(());
90        }
91
92        let output = Command::new("npx")
93            .arg("@biomejs/biome")
94            .arg("format")
95            .arg("--write")
96            .args(&file_paths)
97            .output();
98
99        match output {
100            Ok(_) => Ok(()),
101            Err(e) => {
102                // Silently fail if biome is not available
103                eprintln!("Warning: Failed to run biome: {}", e);
104                Ok(())
105            }
106        }
107    }
108}