Skip to content

Commit f5c94ee

Browse files
committed
feat(install): add bun as a package manager (#557)
Add bun as the 4th supported package manager alongside pnpm, npm, and yarn. Bun is added only as a package manager — runtime support is not planned. Rust core: - Add `Bun` variant to `PackageManagerType` enum - Detect bun via `packageManager` field, `bun.lock`, `bun.lockb`, `bunfig.toml` - Download platform-specific native binary from `@oven/bun-{os}-{arch}` npm packages - Add native binary shim support (non-Node.js wrappers for sh/cmd/ps1) - Add `PackageManagerType::Bun` arms to all 30 command files - Add bun to interactive package manager selection menu Global CLI & NAPI: - Add `"bun"` to `PACKAGE_MANAGER_TOOLS` in shim dispatch - Add `"bun" => PackageManagerType::Bun` in NAPI binding TypeScript: - Add `bun` to `PackageManager` type and selection prompt - Add `bunx` as DLX command runner - Handle bun in monorepo templates and migration (overrides, no catalog) RFCs: - Update all 11 package-manager RFCs with bun command mappings
1 parent df57cf4 commit f5c94ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1553
-419
lines changed

crates/vite_global_cli/src/shim/dispatch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const RECURSION_ENV_VAR: &str = env_vars::VITE_PLUS_TOOL_RECURSION;
2727

2828
/// Package manager tools that should resolve Node.js version from the project context
2929
/// rather than using the install-time version.
30-
const PACKAGE_MANAGER_TOOLS: &[&str] = &["pnpm", "yarn"];
30+
const PACKAGE_MANAGER_TOOLS: &[&str] = &["pnpm", "yarn", "bun"];
3131

3232
fn is_package_manager_tool(tool: &str) -> bool {
3333
PACKAGE_MANAGER_TOOLS.contains(&tool)

crates/vite_install/src/commands/add.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{collections::HashMap, process::ExitStatus};
33
use vite_command::run_command;
44
use vite_error::Error;
55
use vite_path::AbsolutePath;
6+
use vite_shared::output;
67

78
use crate::package_manager::{
89
PackageManager, PackageManagerType, ResolveCommandResult, format_path_env,
@@ -195,6 +196,47 @@ impl PackageManager {
195196
args.push("--save-exact".into());
196197
}
197198
}
199+
PackageManagerType::Bun => {
200+
bin_name = "bun".into();
201+
args.push("add".into());
202+
203+
if let Some(save_dependency_type) = options.save_dependency_type {
204+
match save_dependency_type {
205+
SaveDependencyType::Production => {
206+
// default, no flag needed
207+
}
208+
SaveDependencyType::Dev => {
209+
args.push("--dev".into());
210+
}
211+
SaveDependencyType::Peer => {
212+
args.push("--peer".into());
213+
}
214+
SaveDependencyType::Optional => {
215+
args.push("--optional".into());
216+
}
217+
}
218+
}
219+
if options.save_exact {
220+
args.push("--exact".into());
221+
}
222+
if let Some(filters) = options.filters {
223+
if !filters.is_empty() {
224+
output::warn("bun add does not support --filter");
225+
}
226+
}
227+
if options.workspace_root {
228+
output::warn("bun add does not support --workspace-root");
229+
}
230+
if options.workspace_only {
231+
output::warn("bun add does not support --workspace-only");
232+
}
233+
if options.save_catalog_name.is_some() {
234+
output::warn("bun add does not support --save-catalog-name");
235+
}
236+
if options.allow_build.is_some() {
237+
output::warn("bun add does not support --allow-build");
238+
}
239+
}
198240
}
199241

200242
if let Some(pass_through_args) = options.pass_through_args {

crates/vite_install/src/commands/audit.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,29 @@ impl PackageManager {
142142
}
143143
}
144144
}
145+
PackageManagerType::Bun => {
146+
bin_name = "bun".into();
147+
args.push("pm".into());
148+
args.push("audit".into());
149+
150+
if options.fix {
151+
output::warn("bun pm audit does not support --fix");
152+
return None;
153+
}
154+
155+
if let Some(level) = options.level {
156+
args.push("--level".into());
157+
args.push(level.to_string());
158+
}
159+
160+
if options.production {
161+
output::warn("--production not supported by bun pm audit, ignoring flag");
162+
}
163+
164+
if options.json {
165+
args.push("--json".into());
166+
}
167+
}
145168
}
146169

147170
// Add pass-through args

crates/vite_install/src/commands/cache.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,28 @@ impl PackageManager {
117117
}
118118
}
119119
}
120+
PackageManagerType::Bun => {
121+
bin_name = "bun".into();
122+
123+
match options.subcommand {
124+
"dir" | "path" => {
125+
args.push("pm".into());
126+
args.push("cache".into());
127+
}
128+
"clean" => {
129+
args.push("pm".into());
130+
args.push("cache".into());
131+
args.push("rm".into());
132+
}
133+
_ => {
134+
output::warn(&format!(
135+
"bun pm cache subcommand '{}' not supported",
136+
options.subcommand
137+
));
138+
return None;
139+
}
140+
}
141+
}
120142
}
121143

122144
// Add pass-through args

crates/vite_install/src/commands/config.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,32 @@ impl PackageManager {
127127
}
128128
}
129129
}
130+
PackageManagerType::Bun => {
131+
output::warn(
132+
"bun uses bunfig.toml for configuration, not a config command. Falling back to npm config.",
133+
);
134+
135+
// Fall back to npm config
136+
args.push("config".into());
137+
args.push(options.subcommand.to_string());
138+
139+
if let Some(key) = options.key {
140+
args.push(key.to_string());
141+
}
142+
143+
if let Some(value) = options.value {
144+
args.push(value.to_string());
145+
}
146+
147+
if options.json {
148+
args.push("--json".into());
149+
}
150+
151+
if let Some(location) = options.location {
152+
args.push("--location".into());
153+
args.push(location.to_string());
154+
}
155+
}
130156
}
131157

132158
// Add pass-through args

crates/vite_install/src/commands/dedupe.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{collections::HashMap, process::ExitStatus};
33
use vite_command::run_command;
44
use vite_error::Error;
55
use vite_path::AbsolutePath;
6+
use vite_shared::output;
67

78
use crate::package_manager::{
89
PackageManager, PackageManagerType, ResolveCommandResult, format_path_env,
@@ -63,6 +64,11 @@ impl PackageManager {
6364
args.push("--dry-run".into());
6465
}
6566
}
67+
PackageManagerType::Bun => {
68+
bin_name = "bun".into();
69+
output::warn("bun does not support dedupe, falling back to bun install");
70+
args.push("install".into());
71+
}
6672
}
6773

6874
// Add pass-through args

crates/vite_install/src/commands/deprecate.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use vite_command::run_command;
44
use vite_error::Error;
55
use vite_path::AbsolutePath;
66

7-
use crate::package_manager::{PackageManager, ResolveCommandResult, format_path_env};
7+
use vite_shared::output;
8+
9+
use crate::package_manager::{PackageManager, PackageManagerType, ResolveCommandResult, format_path_env};
810

911
/// Options for the deprecate command.
1012
#[derive(Debug, Default)]
@@ -31,6 +33,7 @@ impl PackageManager {
3133

3234
/// Resolve the deprecate command.
3335
/// All package managers delegate to npm deprecate.
36+
/// Bun does not support deprecate, falls back to npm.
3437
#[must_use]
3538
pub fn resolve_deprecate_command(
3639
&self,
@@ -40,6 +43,12 @@ impl PackageManager {
4043
let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]);
4144
let mut args: Vec<String> = Vec::new();
4245

46+
if self.client == PackageManagerType::Bun {
47+
output::warn(
48+
"bun does not support the deprecate command, falling back to npm deprecate",
49+
);
50+
}
51+
4352
args.push("deprecate".into());
4453
args.push(options.package.to_string());
4554
args.push(options.message.to_string());

crates/vite_install/src/commands/dist_tag.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{collections::HashMap, process::ExitStatus};
33
use vite_command::run_command;
44
use vite_error::Error;
55
use vite_path::AbsolutePath;
6+
use vite_shared::output;
67

78
use crate::package_manager::{
89
PackageManager, PackageManagerType, ResolveCommandResult, format_path_env,
@@ -64,6 +65,13 @@ impl PackageManager {
6465
args.push("tag".into());
6566
}
6667
}
68+
PackageManagerType::Bun => {
69+
output::warn(
70+
"bun does not support dist-tag, falling back to npm dist-tag",
71+
);
72+
bin_name = "npm".into();
73+
args.push("dist-tag".into());
74+
}
6775
}
6876

6977
match &options.subcommand {

crates/vite_install/src/commands/dlx.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ impl PackageManager {
5252
self.resolve_yarn_dlx(options, envs)
5353
}
5454
}
55+
PackageManagerType::Bun => self.resolve_bun_dlx(options, envs),
5556
}
5657
}
5758

@@ -187,6 +188,31 @@ impl PackageManager {
187188
let args = build_npx_args(options);
188189
ResolveCommandResult { bin_path: "npx".into(), args, envs }
189190
}
191+
192+
fn resolve_bun_dlx(
193+
&self,
194+
options: &DlxCommandOptions,
195+
envs: HashMap<String, String>,
196+
) -> ResolveCommandResult {
197+
let mut args = Vec::new();
198+
199+
// bunx is the dlx equivalent, no subcommand needed
200+
// Add package spec
201+
args.push(options.package_spec.into());
202+
203+
// Add command arguments
204+
args.extend(options.args.iter().cloned());
205+
206+
// Warn about unsupported flags
207+
if !options.packages.is_empty() {
208+
output::warn("bunx does not support --package");
209+
}
210+
if options.shell_mode {
211+
output::warn("bunx does not support shell mode (-c)");
212+
}
213+
214+
ResolveCommandResult { bin_path: "bunx".into(), args, envs }
215+
}
190216
}
191217

192218
/// Build npx command-line arguments from dlx options.

crates/vite_install/src/commands/fund.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use vite_command::run_command;
44
use vite_error::Error;
55
use vite_path::AbsolutePath;
66

7-
use crate::package_manager::{PackageManager, ResolveCommandResult, format_path_env};
7+
use vite_shared::output;
8+
9+
use crate::package_manager::{PackageManager, PackageManagerType, ResolveCommandResult, format_path_env};
810

911
/// Options for the fund command.
1012
#[derive(Debug, Default)]
@@ -28,12 +30,19 @@ impl PackageManager {
2830

2931
/// Resolve the fund command.
3032
/// All package managers delegate to npm fund.
33+
/// Bun does not support fund, falls back to npm.
3134
#[must_use]
3235
pub fn resolve_fund_command(&self, options: &FundCommandOptions) -> ResolveCommandResult {
3336
let bin_name: String = "npm".to_string();
3437
let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]);
3538
let mut args: Vec<String> = Vec::new();
3639

40+
if self.client == PackageManagerType::Bun {
41+
output::warn(
42+
"bun does not support the fund command, falling back to npm fund",
43+
);
44+
}
45+
3746
args.push("fund".into());
3847

3948
if options.json {

0 commit comments

Comments
 (0)