diff --git a/.changes/support-bun.md b/.changes/support-bun.md new file mode 100644 index 000000000000..729fd24252f3 --- /dev/null +++ b/.changes/support-bun.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:feat' +'@tauri-apps/cli': 'patch:feat' +--- + +Support Bun package manager in CLI diff --git a/tooling/cli/node/README.md b/tooling/cli/node/README.md index bf961aa46322..7cf8ea03b017 100644 --- a/tooling/cli/node/README.md +++ b/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. diff --git a/tooling/cli/node/tauri.js b/tooling/cli/node/tauri.js index 1516306dfb44..8ee5b93cd5ba 100755 --- a/tooling/cli/node/tauri.js +++ b/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) }) diff --git a/tooling/cli/src/completions.rs b/tooling/cli/src/completions.rs index 874e5d2a0ce2..585ee6f508a4 100644 --- a/tooling/cli/src/completions.rs +++ b/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 { 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 { "complete -F _cargo -o bashdefault -o default {} tauri\n", if manager == &"npm" { "npm run" + } else if manager == &"bun" { + "bun run" } else { manager } diff --git a/tooling/cli/src/info/env_nodejs.rs b/tooling/cli/src/info/env_nodejs.rs index fa8c77fd4c9f..22ef3fd83269 100644 --- a/tooling/cli/src/info/env_nodejs.rs +++ b/tooling/cli/src/info/env_nodejs.rs @@ -88,6 +88,28 @@ pub fn items(metadata: &VersionMetadata) -> (Vec, Option) { || 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 diff --git a/tooling/cli/src/info/packages_nodejs.rs b/tooling/cli/src/info/packages_nodejs.rs index 247c5909d1e4..217ce34e2547 100644 --- a/tooling/cli/src/info/packages_nodejs.rs +++ b/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 { + 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>( .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>(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>(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>(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>(app_dir_entries: &[T]) -> PackageManager { PackageManager::Npm } else if use_pnpm { PackageManager::Pnpm + } else if use_bun { + PackageManager::Bun } else { PackageManager::Yarn }