|
1 | | -#![allow(clippy::disallowed_macros, clippy::print_stdout)] |
| 1 | +#![allow(clippy::disallowed_macros)] |
| 2 | +#![cfg_attr(test, allow(clippy::print_stdout))] |
2 | 3 |
|
3 | 4 | use std::{ |
4 | 5 | collections::HashMap, |
5 | 6 | env, fmt, |
6 | 7 | fs::{self, File}, |
7 | | - io::{self, BufReader, Write}, |
| 8 | + io::{self, BufReader}, |
8 | 9 | path::{Path, PathBuf}, |
9 | 10 | time::Duration, |
10 | 11 | }; |
11 | 12 |
|
12 | | -use crossterm::{ |
13 | | - cursor, |
14 | | - event::{self, Event, KeyCode, KeyEvent, KeyEventKind}, |
15 | | - execute, |
16 | | - style::{Color, Print, ResetColor, SetForegroundColor}, |
17 | | - terminal, |
18 | | -}; |
19 | 13 | use semver::Version; |
20 | 14 | use serde::{Deserialize, Serialize}; |
21 | 15 | use tokio::fs::remove_dir_all; |
@@ -260,21 +254,6 @@ impl PackageManagerBuilder { |
260 | 254 | bin_prefix: install_dir.join("bin"), |
261 | 255 | }) |
262 | 256 | } |
263 | | - |
264 | | - /// Build the package manager with default package manager. |
265 | | - /// If the package manager is not specified, prompt the user to select a package manager. |
266 | | - pub async fn build_with_default(&self) -> Result<PackageManager, Error> { |
267 | | - let package_manager = match self.build().await { |
268 | | - Ok(pm) => pm, |
269 | | - Err(Error::UnrecognizedPackageManager) => { |
270 | | - // Prompt user to select a package manager |
271 | | - let selected_type = prompt_package_manager_selection()?; |
272 | | - Self::new(&self.cwd).package_manager_type(selected_type).build().await? |
273 | | - } |
274 | | - Err(e) => return Err(e), |
275 | | - }; |
276 | | - Ok(package_manager) |
277 | | - } |
278 | 257 | } |
279 | 258 |
|
280 | 259 | // Version gates and migration must use the npm on PATH, not the latest registry release. |
@@ -1876,223 +1855,6 @@ async fn create_bun_shim_files(bin_prefix: &AbsolutePath) -> Result<(), Error> { |
1876 | 1855 | Ok(()) |
1877 | 1856 | } |
1878 | 1857 |
|
1879 | | -use vp_shared::is_ci_environment; |
1880 | | - |
1881 | | -/// Interactive menu for selecting a package manager with keyboard navigation |
1882 | | -fn interactive_package_manager_menu() -> Result<PackageManagerType, Error> { |
1883 | | - let options = [ |
1884 | | - ("pnpm (recommended)", PackageManagerType::Pnpm), |
1885 | | - ("npm", PackageManagerType::Npm), |
1886 | | - ("yarn", PackageManagerType::Yarn), |
1887 | | - ("bun", PackageManagerType::Bun), |
1888 | | - ]; |
1889 | | - |
1890 | | - let mut selected_index = 0; |
1891 | | - |
1892 | | - // Print header and instructions with proper line breaks |
1893 | | - println!("\nNo package manager detected. Please select one:"); |
1894 | | - println!( |
1895 | | - " Use ↑↓ arrows to navigate, Enter to select, 1-{} for quick selection", |
1896 | | - options.len() |
1897 | | - ); |
1898 | | - println!(" Press Esc, q, or Ctrl+C to cancel installation\n"); |
1899 | | - |
1900 | | - // Enable raw mode for keyboard input |
1901 | | - terminal::enable_raw_mode()?; |
1902 | | - |
1903 | | - // Clear the selection area and hide cursor |
1904 | | - execute!(io::stdout(), cursor::Hide)?; |
1905 | | - |
1906 | | - let result = loop { |
1907 | | - // Display menu with current selection |
1908 | | - for (i, (name, _)) in options.iter().enumerate() { |
1909 | | - execute!(io::stdout(), cursor::MoveToColumn(2))?; |
1910 | | - |
1911 | | - if i == selected_index { |
1912 | | - // Highlight selected item |
1913 | | - execute!( |
1914 | | - io::stdout(), |
1915 | | - SetForegroundColor(Color::Blue), |
1916 | | - Print("▶ "), |
1917 | | - Print(format!("[{}] ", i + 1)), |
1918 | | - Print(name), |
1919 | | - ResetColor, |
1920 | | - Print(" ← ") |
1921 | | - )?; |
1922 | | - } else { |
1923 | | - execute!( |
1924 | | - io::stdout(), |
1925 | | - Print(" "), |
1926 | | - SetForegroundColor(Color::DarkGrey), |
1927 | | - Print(format!("[{}] ", i + 1)), |
1928 | | - ResetColor, |
1929 | | - Print(name), |
1930 | | - Print(" ") |
1931 | | - )?; |
1932 | | - } |
1933 | | - |
1934 | | - if i < options.len() - 1 { |
1935 | | - execute!(io::stdout(), Print("\n"))?; |
1936 | | - } |
1937 | | - } |
1938 | | - |
1939 | | - // Move cursor back up for next iteration |
1940 | | - if options.len() > 1 { |
1941 | | - execute!(io::stdout(), cursor::MoveUp((options.len() - 1) as u16))?; |
1942 | | - } |
1943 | | - |
1944 | | - // Read keyboard input, skipping non-Press events (e.g. Release on Windows) |
1945 | | - let (code, modifiers) = loop { |
1946 | | - if let Event::Key(KeyEvent { code, modifiers, kind, .. }) = event::read()? |
1947 | | - && kind == KeyEventKind::Press |
1948 | | - { |
1949 | | - break (code, modifiers); |
1950 | | - } |
1951 | | - }; |
1952 | | - |
1953 | | - match code { |
1954 | | - // Handle Ctrl+C for exit |
1955 | | - KeyCode::Char('c') if modifiers.contains(event::KeyModifiers::CONTROL) => { |
1956 | | - // Clean up terminal before exiting |
1957 | | - terminal::disable_raw_mode()?; |
1958 | | - execute!( |
1959 | | - io::stdout(), |
1960 | | - cursor::Show, |
1961 | | - cursor::MoveDown(options.len() as u16), |
1962 | | - Print("\n\n"), |
1963 | | - SetForegroundColor(Color::Yellow), |
1964 | | - Print("⚠ Installation cancelled by user\n"), |
1965 | | - ResetColor |
1966 | | - )?; |
1967 | | - return Err(Error::UserCancelled); |
1968 | | - } |
1969 | | - KeyCode::Up => { |
1970 | | - selected_index = selected_index.saturating_sub(1); |
1971 | | - } |
1972 | | - KeyCode::Down if selected_index < options.len() - 1 => { |
1973 | | - selected_index += 1; |
1974 | | - } |
1975 | | - KeyCode::Enter | KeyCode::Char(' ') => { |
1976 | | - break Ok(options[selected_index].1); |
1977 | | - } |
1978 | | - KeyCode::Char('1') => { |
1979 | | - break Ok(options[0].1); |
1980 | | - } |
1981 | | - KeyCode::Char('2') if options.len() > 1 => { |
1982 | | - break Ok(options[1].1); |
1983 | | - } |
1984 | | - KeyCode::Char('3') if options.len() > 2 => { |
1985 | | - break Ok(options[2].1); |
1986 | | - } |
1987 | | - KeyCode::Char('4') if options.len() > 3 => { |
1988 | | - break Ok(options[3].1); |
1989 | | - } |
1990 | | - KeyCode::Esc | KeyCode::Char('q') => { |
1991 | | - // Exit on escape/quit |
1992 | | - terminal::disable_raw_mode()?; |
1993 | | - execute!( |
1994 | | - io::stdout(), |
1995 | | - cursor::Show, |
1996 | | - cursor::MoveDown(options.len() as u16), |
1997 | | - Print("\n\n"), |
1998 | | - SetForegroundColor(Color::Yellow), |
1999 | | - Print("⚠ Installation cancelled by user\n"), |
2000 | | - ResetColor |
2001 | | - )?; |
2002 | | - return Err(Error::UserCancelled); |
2003 | | - } |
2004 | | - _ => {} |
2005 | | - } |
2006 | | - }; |
2007 | | - |
2008 | | - // Clean up: disable raw mode and show cursor |
2009 | | - terminal::disable_raw_mode()?; |
2010 | | - execute!(io::stdout(), cursor::Show, cursor::MoveDown(options.len() as u16), Print("\n"))?; |
2011 | | - |
2012 | | - // Print selection confirmation |
2013 | | - if let Ok(pm) = &result { |
2014 | | - let name = match pm { |
2015 | | - PackageManagerType::Pnpm => "pnpm", |
2016 | | - PackageManagerType::Npm => "npm", |
2017 | | - PackageManagerType::Yarn => "yarn", |
2018 | | - PackageManagerType::Bun => "bun", |
2019 | | - }; |
2020 | | - println!("\n✓ Selected package manager: {name}\n"); |
2021 | | - } |
2022 | | - |
2023 | | - result |
2024 | | -} |
2025 | | - |
2026 | | -/// Prompt the user to select a package manager |
2027 | | -fn prompt_package_manager_selection() -> Result<PackageManagerType, Error> { |
2028 | | - // In CI environment, automatically use pnpm without prompting |
2029 | | - if is_ci_environment() { |
2030 | | - tracing::info!("CI environment detected. Using default package manager: pnpm"); |
2031 | | - return Ok(PackageManagerType::Pnpm); |
2032 | | - } |
2033 | | - |
2034 | | - // Check if stdin is a TTY (terminal) - if not, use default |
2035 | | - if !vp_shared::is_stdin_terminal() { |
2036 | | - tracing::info!("Non-interactive environment detected. Using default package manager: pnpm"); |
2037 | | - return Ok(PackageManagerType::Pnpm); |
2038 | | - } |
2039 | | - |
2040 | | - // Try interactive menu first, fall back to simple prompt on error |
2041 | | - match interactive_package_manager_menu() { |
2042 | | - Ok(pm) => Ok(pm), |
2043 | | - Err(err) => { |
2044 | | - match err { |
2045 | | - Error::UserCancelled => Err(err), |
2046 | | - // Fallback to simple text prompt if interactive menu fails |
2047 | | - _ => simple_text_prompt(), |
2048 | | - } |
2049 | | - } |
2050 | | - } |
2051 | | -} |
2052 | | - |
2053 | | -/// Simple text-based prompt as fallback |
2054 | | -fn simple_text_prompt() -> Result<PackageManagerType, Error> { |
2055 | | - let managers = [ |
2056 | | - ("pnpm", PackageManagerType::Pnpm), |
2057 | | - ("npm", PackageManagerType::Npm), |
2058 | | - ("yarn", PackageManagerType::Yarn), |
2059 | | - ("bun", PackageManagerType::Bun), |
2060 | | - ]; |
2061 | | - |
2062 | | - println!("\nNo package manager detected. Please select one:"); |
2063 | | - println!("────────────────────────────────────────────────"); |
2064 | | - |
2065 | | - for (i, (name, _)) in managers.iter().enumerate() { |
2066 | | - if i == 0 { |
2067 | | - println!(" [{}] {} (recommended)", i + 1, name); |
2068 | | - } else { |
2069 | | - println!(" [{}] {}", i + 1, name); |
2070 | | - } |
2071 | | - } |
2072 | | - |
2073 | | - print!("\nEnter your choice (1-{}) [default: 1]: ", managers.len()); |
2074 | | - io::stdout().flush()?; |
2075 | | - |
2076 | | - let mut input = String::new(); |
2077 | | - io::stdin().read_line(&mut input)?; |
2078 | | - |
2079 | | - let choice = input.trim(); |
2080 | | - let index = if choice.is_empty() { |
2081 | | - 0 // Default to pnpm |
2082 | | - } else { |
2083 | | - choice |
2084 | | - .parse::<usize>() |
2085 | | - .ok() |
2086 | | - .and_then(|n| if n > 0 && n <= managers.len() { Some(n - 1) } else { None }) |
2087 | | - .unwrap_or(0) // Default to pnpm if invalid input |
2088 | | - }; |
2089 | | - |
2090 | | - let (name, selected_type) = &managers[index]; |
2091 | | - println!("✓ Selected package manager: {name}\n"); |
2092 | | - |
2093 | | - Ok(*selected_type) |
2094 | | -} |
2095 | | - |
2096 | 1858 | #[cfg(test)] |
2097 | 1859 | mod tests { |
2098 | 1860 | use std::{fs, time::UNIX_EPOCH}; |
|
0 commit comments