packages_nodejs.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use super::SectionItem;
  5. use super::{env_nodejs::manager_version, VersionMetadata};
  6. use colored::Colorize;
  7. use serde::Deserialize;
  8. use std::path::PathBuf;
  9. use crate::helpers::{cross_command, npm::PackageManager};
  10. #[derive(Deserialize)]
  11. struct YarnVersionInfo {
  12. data: Vec<String>,
  13. }
  14. pub fn npm_latest_version(pm: &PackageManager, name: &str) -> crate::Result<Option<String>> {
  15. match pm {
  16. PackageManager::Yarn => {
  17. let mut cmd = cross_command("yarn");
  18. let output = cmd
  19. .arg("info")
  20. .arg(name)
  21. .args(["version", "--json"])
  22. .output()?;
  23. if output.status.success() {
  24. let stdout = String::from_utf8_lossy(&output.stdout);
  25. let info: YarnVersionInfo = serde_json::from_str(&stdout)?;
  26. Ok(Some(info.data.last().unwrap().to_string()))
  27. } else {
  28. Ok(None)
  29. }
  30. }
  31. PackageManager::YarnBerry => {
  32. let mut cmd = cross_command("yarn");
  33. let output = cmd
  34. .arg("npm")
  35. .arg("info")
  36. .arg(name)
  37. .args(["--fields", "version", "--json"])
  38. .output()?;
  39. if output.status.success() {
  40. let info: crate::PackageJson =
  41. serde_json::from_reader(std::io::Cursor::new(output.stdout)).unwrap();
  42. Ok(info.version)
  43. } else {
  44. Ok(None)
  45. }
  46. }
  47. PackageManager::Npm => {
  48. let mut cmd = cross_command("npm");
  49. let output = cmd.arg("show").arg(name).arg("version").output()?;
  50. if output.status.success() {
  51. let stdout = String::from_utf8_lossy(&output.stdout);
  52. Ok(Some(stdout.replace('\n', "")))
  53. } else {
  54. Ok(None)
  55. }
  56. }
  57. PackageManager::Pnpm => {
  58. let mut cmd = cross_command("pnpm");
  59. let output = cmd.arg("info").arg(name).arg("version").output()?;
  60. if output.status.success() {
  61. let stdout = String::from_utf8_lossy(&output.stdout);
  62. Ok(Some(stdout.replace('\n', "")))
  63. } else {
  64. Ok(None)
  65. }
  66. }
  67. // Bun doesn't support `info` command
  68. PackageManager::Bun => {
  69. let mut cmd = cross_command("npm");
  70. let output = cmd.arg("show").arg(name).arg("version").output()?;
  71. if output.status.success() {
  72. let stdout = String::from_utf8_lossy(&output.stdout);
  73. Ok(Some(stdout.replace('\n', "")))
  74. } else {
  75. Ok(None)
  76. }
  77. }
  78. }
  79. }
  80. pub fn package_manager(app_dir: &PathBuf) -> PackageManager {
  81. let mut use_npm = false;
  82. let mut use_pnpm = false;
  83. let mut use_yarn = false;
  84. let mut use_bun = false;
  85. for entry in std::fs::read_dir(app_dir)
  86. .unwrap()
  87. .map(|e| e.unwrap().file_name().to_string_lossy().into_owned())
  88. {
  89. match entry.as_str() {
  90. "pnpm-lock.yaml" => use_pnpm = true,
  91. "package-lock.json" => use_npm = true,
  92. "yarn.lock" => use_yarn = true,
  93. "bun.lockb" => use_bun = true,
  94. _ => {}
  95. }
  96. }
  97. if !use_npm && !use_pnpm && !use_yarn && !use_bun {
  98. println!(
  99. "{}: no lock files found, defaulting to npm",
  100. "WARNING".yellow()
  101. );
  102. return PackageManager::Npm;
  103. }
  104. let mut found = Vec::new();
  105. if use_npm {
  106. found.push(PackageManager::Npm);
  107. }
  108. if use_pnpm {
  109. found.push(PackageManager::Pnpm);
  110. }
  111. if use_yarn {
  112. found.push(PackageManager::Yarn);
  113. }
  114. if use_bun {
  115. found.push(PackageManager::Bun);
  116. }
  117. if found.len() > 1 {
  118. let pkg_manger = found[0];
  119. println!(
  120. "{}: Only one package manager should be used, but found {}.\n Please remove unused package manager lock files, will use {} for now!",
  121. "WARNING".yellow(),
  122. found.iter().map(ToString::to_string).collect::<Vec<_>>().join(" and "),
  123. pkg_manger
  124. );
  125. return pkg_manger;
  126. }
  127. if use_npm {
  128. PackageManager::Npm
  129. } else if use_pnpm {
  130. PackageManager::Pnpm
  131. } else if use_bun {
  132. PackageManager::Bun
  133. } else if manager_version("yarn")
  134. .map(|v| v.chars().next().map(|c| c > '1').unwrap_or_default())
  135. .unwrap_or(false)
  136. {
  137. PackageManager::YarnBerry
  138. } else {
  139. PackageManager::Yarn
  140. }
  141. }
  142. pub fn items(
  143. app_dir: Option<&PathBuf>,
  144. package_manager: PackageManager,
  145. metadata: &VersionMetadata,
  146. ) -> Vec<SectionItem> {
  147. let mut items = Vec::new();
  148. if let Some(app_dir) = app_dir {
  149. for (package, version) in [
  150. ("@tauri-apps/api", None),
  151. ("@tauri-apps/cli", Some(metadata.js_cli.version.clone())),
  152. ] {
  153. let app_dir = app_dir.clone();
  154. let item = nodejs_section_item(package.into(), version, app_dir, package_manager);
  155. items.push(item);
  156. }
  157. }
  158. items
  159. }
  160. pub fn nodejs_section_item(
  161. package: String,
  162. version: Option<String>,
  163. app_dir: PathBuf,
  164. package_manager: PackageManager,
  165. ) -> SectionItem {
  166. SectionItem::new().action(move || {
  167. let version = version.clone().unwrap_or_else(|| {
  168. package_manager
  169. .current_package_version(&package, &app_dir)
  170. .unwrap_or_default()
  171. .unwrap_or_default()
  172. });
  173. let latest_ver = super::packages_nodejs::npm_latest_version(&package_manager, &package)
  174. .unwrap_or_default()
  175. .unwrap_or_default();
  176. if version.is_empty() {
  177. format!("{} {}: not installed!", package, "".green())
  178. } else {
  179. format!(
  180. "{} {}: {}{}",
  181. package,
  182. "".dimmed(),
  183. version,
  184. if !(version.is_empty() || latest_ver.is_empty()) {
  185. let version = semver::Version::parse(version.as_str()).unwrap();
  186. let target_version = semver::Version::parse(latest_ver.as_str()).unwrap();
  187. if version < target_version {
  188. format!(" ({}, latest: {})", "outdated".yellow(), latest_ver.green())
  189. } else {
  190. "".into()
  191. }
  192. } else {
  193. "".into()
  194. }
  195. )
  196. }
  197. .into()
  198. })
  199. }