syncable_cli/analyzer/tool_management/installers/
javascript.rs

1use super::common::InstallationUtils;
2use crate::analyzer::tool_management::ToolDetector;
3use crate::error::{AnalysisError, IaCGeneratorError, Result};
4use log::{info, warn};
5use std::collections::HashMap;
6use std::process::Command;
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(
61        "powershell",
62        &["-Command", "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(
70            AnalysisError::DependencyParsing {
71                file: "bun installation".to_string(),
72                reason: "PowerShell installation failed".to_string(),
73            },
74        ))
75    }
76}
77
78/// Install bun on Unix systems using curl
79fn install_bun_unix() -> Result<()> {
80    info!("🐧 Installing bun on Unix using curl...");
81
82    let output = Command::new("curl")
83        .args(["-fsSL", "https://bun.sh/install"])
84        .stdout(std::process::Stdio::piped())
85        .spawn()
86        .and_then(|curl_process| {
87            Command::new("bash")
88                .stdin(curl_process.stdout.unwrap())
89                .output()
90        })
91        .map_err(|e| {
92            IaCGeneratorError::Analysis(AnalysisError::DependencyParsing {
93                file: "bun installation".to_string(),
94                reason: format!("Failed to execute curl | bash installer: {}", e),
95            })
96        })?;
97
98    if output.status.success() {
99        info!("✅ Bun installed successfully via curl");
100        info!(
101            "💡 Note: You may need to restart your terminal or run 'source ~/.bashrc' to use bun"
102        );
103        Ok(())
104    } else {
105        let stderr = String::from_utf8_lossy(&output.stderr);
106        Err(IaCGeneratorError::Analysis(
107            AnalysisError::DependencyParsing {
108                file: "bun installation".to_string(),
109                reason: format!("curl installation failed: {}", stderr),
110            },
111        ))
112    }
113}
114
115/// Ensure yarn is available
116pub fn ensure_yarn(
117    tool_detector: &mut ToolDetector,
118    installed_tools: &mut HashMap<String, bool>,
119) -> Result<()> {
120    if tool_detector.detect_tool("yarn").available {
121        return Ok(());
122    }
123
124    info!("🔧 Installing yarn package manager...");
125
126    let success = InstallationUtils::execute_command("npm", &["install", "-g", "yarn"])?;
127
128    if success {
129        info!("✅ yarn installed successfully");
130        installed_tools.insert("yarn".to_string(), true);
131        tool_detector.clear_cache();
132    } else {
133        warn!("❌ Failed to install yarn via npm");
134        warn!("📦 Please install yarn manually: https://yarnpkg.com/");
135    }
136
137    Ok(())
138}
139
140/// Ensure pnpm is available
141pub fn ensure_pnpm(
142    tool_detector: &mut ToolDetector,
143    installed_tools: &mut HashMap<String, bool>,
144) -> Result<()> {
145    if tool_detector.detect_tool("pnpm").available {
146        return Ok(());
147    }
148
149    info!("🔧 Installing pnpm package manager...");
150
151    let success = InstallationUtils::execute_command("npm", &["install", "-g", "pnpm"])?;
152
153    if success {
154        info!("✅ pnpm installed successfully");
155        installed_tools.insert("pnpm".to_string(), true);
156        tool_detector.clear_cache();
157    } else {
158        warn!("❌ Failed to install pnpm via npm");
159        warn!("📦 Please install pnpm manually: https://pnpm.io/");
160    }
161
162    Ok(())
163}