syncable_cli/analyzer/tool_management/installers/
javascript.rs

1use crate::analyzer::tool_management::ToolDetector;
2use crate::error::{AnalysisError, IaCGeneratorError, Result};
3use super::common::InstallationUtils;
4use std::collections::HashMap;
5use std::process::Command;
6use log::{info, warn, debug};
7
8/// Ensure npm is available (comes with Node.js)
9pub fn ensure_npm(
10    tool_detector: &mut ToolDetector,
11    installed_tools: &mut HashMap<String, bool>,
12) -> Result<()> {
13    if tool_detector.detect_tool("npm").available {
14        return Ok(());
15    }
16    
17    warn!("📦 npm not found. Please install Node.js from https://nodejs.org/");
18    warn!("   npm audit is required for JavaScript/TypeScript vulnerability scanning");
19    
20    Ok(()) // Don't fail, just warn
21}
22
23/// Install bun runtime and package manager
24pub fn install_bun(
25    tool_detector: &mut ToolDetector,
26    installed_tools: &mut HashMap<String, bool>,
27) -> Result<()> {
28    if tool_detector.detect_tool("bun").available {
29        return Ok(());
30    }
31    
32    info!("🔧 Installing bun runtime and package manager...");
33    
34    let install_result = if cfg!(target_os = "windows") {
35        install_bun_windows()
36    } else {
37        install_bun_unix()
38    };
39    
40    match install_result {
41        Ok(()) => {
42            info!("✅ Bun installed successfully");
43            tool_detector.clear_cache();
44            installed_tools.insert("bun".to_string(), true);
45            Ok(())
46        }
47        Err(e) => {
48            warn!("❌ Failed to install bun: {}", e);
49            warn!("📦 Please install bun manually from https://bun.sh/");
50            warn!("   Falling back to npm for JavaScript/TypeScript vulnerability scanning");
51            ensure_npm(tool_detector, installed_tools)
52        }
53    }
54}
55
56/// Install bun on Windows using PowerShell
57fn install_bun_windows() -> Result<()> {
58    info!("💻 Installing bun on Windows using PowerShell...");
59    
60    let success = InstallationUtils::execute_command("powershell", &[
61        "-Command",
62        "irm bun.sh/install.ps1 | iex"
63    ])?;
64    
65    if success {
66        info!("✅ Bun installed successfully via PowerShell");
67        Ok(())
68    } else {
69        Err(IaCGeneratorError::Analysis(AnalysisError::DependencyParsing {
70            file: "bun installation".to_string(),
71            reason: "PowerShell installation failed".to_string(),
72        }))
73    }
74}
75
76/// Install bun on Unix systems using curl
77fn install_bun_unix() -> Result<()> {
78    info!("🐧 Installing bun on Unix using curl...");
79    
80    let output = Command::new("curl")
81        .args(&["-fsSL", "https://bun.sh/install"])
82        .stdout(std::process::Stdio::piped())
83        .spawn()
84        .and_then(|curl_process| {
85            Command::new("bash")
86                .stdin(curl_process.stdout.unwrap())
87                .output()
88        })
89        .map_err(|e| IaCGeneratorError::Analysis(AnalysisError::DependencyParsing {
90            file: "bun installation".to_string(),
91            reason: format!("Failed to execute curl | bash installer: {}", e),
92        }))?;
93        
94    if output.status.success() {
95        info!("✅ Bun installed successfully via curl");
96        info!("💡 Note: You may need to restart your terminal or run 'source ~/.bashrc' to use bun");
97        Ok(())
98    } else {
99        let stderr = String::from_utf8_lossy(&output.stderr);
100        Err(IaCGeneratorError::Analysis(AnalysisError::DependencyParsing {
101            file: "bun installation".to_string(),
102            reason: format!("curl installation failed: {}", stderr),
103        }))
104    }
105}
106
107/// Ensure yarn is available
108pub fn ensure_yarn(
109    tool_detector: &mut ToolDetector,
110    installed_tools: &mut HashMap<String, bool>,
111) -> Result<()> {
112    if tool_detector.detect_tool("yarn").available {
113        return Ok(());
114    }
115    
116    info!("🔧 Installing yarn package manager...");
117    
118    let success = InstallationUtils::execute_command("npm", &["install", "-g", "yarn"])?;
119    
120    if success {
121        info!("✅ yarn installed successfully");
122        installed_tools.insert("yarn".to_string(), true);
123        tool_detector.clear_cache();
124    } else {
125        warn!("❌ Failed to install yarn via npm");
126        warn!("📦 Please install yarn manually: https://yarnpkg.com/");
127    }
128    
129    Ok(())
130}
131
132/// Ensure pnpm is available
133pub fn ensure_pnpm(
134    tool_detector: &mut ToolDetector,
135    installed_tools: &mut HashMap<String, bool>,
136) -> Result<()> {
137    if tool_detector.detect_tool("pnpm").available {
138        return Ok(());
139    }
140    
141    info!("🔧 Installing pnpm package manager...");
142    
143    let success = InstallationUtils::execute_command("npm", &["install", "-g", "pnpm"])?;
144    
145    if success {
146        info!("✅ pnpm installed successfully");
147        installed_tools.insert("pnpm".to_string(), true);
148        tool_detector.clear_cache();
149    } else {
150        warn!("❌ Failed to install pnpm via npm");
151        warn!("📦 Please install pnpm manually: https://pnpm.io/");
152    }
153    
154    Ok(())
155}