浏览代码

feat: support Bun package manager (#7723)

* Support bun

* clippy
Colin McDonnell 1 年之前
父节点
当前提交
e152662687

+ 6 - 0
.changes/support-bun.md

@@ -0,0 +1,6 @@
+---
+'tauri-cli': 'patch:feat'
+'@tauri-apps/cli': 'patch:feat'
+---
+
+Support Bun package manager in CLI

+ 1 - 1
tooling/cli/node/README.md

@@ -19,7 +19,7 @@ Tauri is a polyglot and generic system that is very composable and allows engine
 
 Tauri apps can have custom menus and have tray-type interfaces. They can be updated, and are managed by the user's operating system as expected. They are very small, because they use the system's webview. They do not ship a runtime, since the final binary is compiled from rust. This makes the reversing of Tauri apps not a trivial task.
 ## This module
-Written in Typescript and packaged such that it can be used with `npm`, `pnpm`, and `yarn`, this library provides a node.js runner for common tasks when using Tauri, like `yarn tauri dev`. For the most part it is a wrapper around [tauri-cli](https://github.com/tauri-apps/tauri/blob/dev/tooling/cli).
+Written in Typescript and packaged such that it can be used with `npm`, `pnpm`, `yarn`, and `bun`, this library provides a node.js runner for common tasks when using Tauri, like `yarn tauri dev`. For the most part it is a wrapper around [tauri-cli](https://github.com/tauri-apps/tauri/blob/dev/tooling/cli).
 
 To learn more about the details of how all of these pieces fit together, please consult this [ARCHITECTURE.md](https://github.com/tauri-apps/tauri/blob/dev/ARCHITECTURE.md) document.
 

+ 5 - 5
tooling/cli/node/tauri.js

@@ -7,7 +7,7 @@
 const cli = require('./main')
 const path = require('path')
 
-const [bin, script, ...arguments] = process.argv
+const [bin, script, ...args] = process.argv
 const binStem = path.parse(bin).name.toLowerCase()
 
 // We want to make a helpful binary name for the underlying CLI helper, if we
@@ -20,7 +20,7 @@ if (bin === '@tauri-apps/cli') {
 }
 // Even if started by a package manager, the binary will be NodeJS.
 // Some distribution still use "nodejs" as the binary name.
-else if (binStem.match(/(nodejs|node)\-?([0-9]*)*$/g)) {
+else if (binStem.match(/(nodejs|node|bun)\-?([0-9]*)*$/g)) {
   const managerStem = process.env.npm_execpath
     ? path.parse(process.env.npm_execpath).name.toLowerCase()
     : null
@@ -32,7 +32,7 @@ else if (binStem.match(/(nodejs|node)\-?([0-9]*)*$/g)) {
         manager = 'npm'
         break
 
-      // Yarn and pnpm have the same stem name as their bin.
+      // Yarn, pnpm, and bun have the same stem name as their bin.
       // We assume all unknown package managers do as well.
       default:
         manager = managerStem
@@ -48,10 +48,10 @@ else if (binStem.match(/(nodejs|node)\-?([0-9]*)*$/g)) {
   }
 } else {
   // We don't know what started it, assume it's already stripped.
-  arguments.unshift(bin)
+  args.unshift(bin)
 }
 
-cli.run(arguments, binName).catch((err) => {
+cli.run(args, binName).catch((err) => {
   cli.logError(err.message)
   process.exit(1)
 })

+ 4 - 2
tooling/cli/src/completions.rs

@@ -10,7 +10,7 @@ use log::info;
 
 use std::{fs::write, path::PathBuf};
 
-const PKG_MANAGERS: &[&str] = &["cargo", "pnpm", "npm", "yarn"];
+const PKG_MANAGERS: &[&str] = &["cargo", "pnpm", "npm", "yarn", "bun"];
 
 #[derive(Debug, Clone, Parser)]
 #[clap(about = "Shell completions")]
@@ -25,7 +25,7 @@ pub struct Options {
 
 fn completions_for(shell: Shell, manager: &'static str, cmd: Command) -> Vec<u8> {
   let tauri = cmd.name("tauri");
-  let mut command = if manager == "npm" {
+  let mut command = if manager == "npm" || manager == "bun" {
     Command::new(manager)
       .bin_name(manager)
       .subcommand(Command::new("run").subcommand(tauri))
@@ -47,6 +47,8 @@ fn get_completions(shell: Shell, cmd: Command) -> Result<String> {
         "complete -F _cargo -o bashdefault -o default {} tauri\n",
         if manager == &"npm" {
           "npm run"
+        } else if manager == &"bun" {
+          "bun run"
         } else {
           manager
         }

+ 22 - 0
tooling/cli/src/info/env_nodejs.rs

@@ -88,6 +88,28 @@ pub fn items(metadata: &VersionMetadata) -> (Vec<SectionItem>, Option<String>) {
         || None,
         false,
       ),
+      SectionItem::new(
+        || {
+          cross_command("bun")
+            .arg("-v")
+            .output()
+            .map(|o| {
+              if o.status.success() {
+                let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
+                Some((
+                  format!("bun: {}", v.split('\n').next().unwrap()),
+                  Status::Neutral,
+                ))
+              } else {
+                None
+              }
+            })
+            .ok()
+            .unwrap_or_default()
+        },
+        || None,
+        false,
+      ),
       SectionItem::new(
         move || {
           yarn_version_c

+ 33 - 1
tooling/cli/src/info/packages_nodejs.rs

@@ -20,6 +20,7 @@ enum PackageManager {
   Pnpm,
   Yarn,
   YarnBerry,
+  Bun,
 }
 
 impl Display for PackageManager {
@@ -32,6 +33,7 @@ impl Display for PackageManager {
         PackageManager::Pnpm => "pnpm",
         PackageManager::Yarn => "yarn",
         PackageManager::YarnBerry => "yarn berry",
+        PackageManager::Bun => "bun",
       }
     )
   }
@@ -94,6 +96,18 @@ fn npm_latest_version(pm: &PackageManager, name: &str) -> crate::Result<Option<S
         Ok(None)
       }
     }
+    // Bun doesn't support `info` command
+    PackageManager::Bun => {
+      let mut cmd = cross_command("npm");
+
+      let output = cmd.arg("show").arg(name).arg("version").output()?;
+      if output.status.success() {
+        let stdout = String::from_utf8_lossy(&output.stdout);
+        Ok(Some(stdout.replace('\n', "")))
+      } else {
+        Ok(None)
+      }
+    }
   }
 }
 
@@ -139,6 +153,16 @@ fn npm_package_version<P: AsRef<Path>>(
         .output()?,
       None,
     ),
+    // Bun doesn't support `list` command
+    PackageManager::Bun => (
+      cross_command("npm")
+        .arg("list")
+        .arg(name)
+        .args(["version", "--depth", "0"])
+        .current_dir(app_dir)
+        .output()?,
+      None,
+    ),
   };
   if output.status.success() {
     let stdout = String::from_utf8_lossy(&output.stdout);
@@ -158,6 +182,7 @@ fn get_package_manager<T: AsRef<str>>(app_dir_entries: &[T]) -> PackageManager {
   let mut use_npm = false;
   let mut use_pnpm = false;
   let mut use_yarn = false;
+  let mut use_bun = false;
 
   for name in app_dir_entries {
     if name.as_ref() == "package-lock.json" {
@@ -166,10 +191,12 @@ fn get_package_manager<T: AsRef<str>>(app_dir_entries: &[T]) -> PackageManager {
       use_pnpm = true;
     } else if name.as_ref() == "yarn.lock" {
       use_yarn = true;
+    } else if name.as_ref() == "bun.lockb" {
+      use_bun = true;
     }
   }
 
-  if !use_npm && !use_pnpm && !use_yarn {
+  if !use_npm && !use_pnpm && !use_yarn && !use_bun {
     println!(
       "{}: no lock files found, defaulting to npm",
       "WARNING".yellow()
@@ -188,6 +215,9 @@ fn get_package_manager<T: AsRef<str>>(app_dir_entries: &[T]) -> PackageManager {
   if use_yarn {
     found.push(PackageManager::Yarn);
   }
+  if use_bun {
+    found.push(PackageManager::Bun);
+  }
 
   if found.len() > 1 {
     let pkg_manger = found[0];
@@ -204,6 +234,8 @@ fn get_package_manager<T: AsRef<str>>(app_dir_entries: &[T]) -> PackageManager {
     PackageManager::Npm
   } else if use_pnpm {
     PackageManager::Pnpm
+  } else if use_bun {
+    PackageManager::Bun
   } else {
     PackageManager::Yarn
   }