1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! Package manager integration — mirrors `bun_runtime::cli::install_command::install_with_cli()`.
//!
//! Calls the same `bun_install` functions as Bun's upstream CLI, with identical
//! control flow. The only difference is the entry point: Bun enters via
//! `InstallCommand::exec(ctx)` (pub(crate) in bun_runtime), we enter directly
//! from `bao_bin`'s clap dispatch through `bao_runtime::run_install()`.
use bun_core::{Global, Output};
use bun_install::package_manager_real::Command;
use bun_install::package_manager_real::{
CommandLineArguments, PackageManager, ROOT_PACKAGE_JSON_PATH, Subcommand, install_with_manager,
update_package_json_and_install_with_manager,
};
/// Execute `bao install` / `bao add`.
///
/// This is the `bao_runtime` equivalent of
/// `bun_runtime::cli::install_command::install_with_cli()`.
///
/// `CommandLineArguments::parse` reads from `bun_core::argv()` (falls back to
/// `std::env::args_os()`), so the caller's argv must contain the install
/// subcommand and any package arguments.
///
/// Callers should invoke `force_link_bun_install()` before this to ensure the
/// `__bun_dispatch__` symbols are linked in.
pub fn run_install() -> Result<(), i32> {
let mut ctx = Command::ContextData::default();
ctx.start_time = bun_core::time::nano_timestamp();
let cli = match CommandLineArguments::parse(Subcommand::Install) {
Ok(cli) => cli,
Err(e) => {
eprintln!("bao install: failed to parse arguments: {}", e);
return Err(1);
}
};
// Same logic as upstream install_with_cli():
// positionals[0] = "install", positionals[1..] = package names → Add.
let subcommand = if cli.positionals.len() > 1 {
Subcommand::Add
} else {
Subcommand::Install
};
// Register the global context pointer so install_with_manager can access it.
Command::GLOBAL_CTX.store(&mut ctx, std::sync::atomic::Ordering::Release);
let (manager, original_cwd) = match PackageManager::init(&mut ctx, cli, Subcommand::Install) {
Ok(m) => m,
Err(e) => {
eprintln!("bao install: initialization failed: {}", e);
return Err(1);
}
};
if subcommand == Subcommand::Add {
manager.subcommand = Subcommand::Add;
if manager.options.should_print_command_name() {
Output::prettyln(format_args!(
"<r><b>bao add <r><d>v{}<r>\n",
Global::package_json_version_with_sha,
));
Output::flush();
}
return match update_package_json_and_install_with_manager(manager, &mut ctx, &original_cwd)
{
Ok(()) => Ok(()),
Err(e) => {
eprintln!("bao add: {}", e);
Err(1)
}
};
}
if manager.options.should_print_command_name() {
Output::prettyln(format_args!(
"<r><b>bao install <r><d>v{}<r>\n",
Global::package_json_version_with_sha,
));
Output::flush();
}
// SAFETY: ROOT_PACKAGE_JSON_PATH is written exactly once inside
// PackageManager::init (above) on this thread; only read thereafter.
let root_package_json_path = unsafe { ROOT_PACKAGE_JSON_PATH.read() };
match install_with_manager(manager, &mut ctx, root_package_json_path, &original_cwd) {
Ok(()) => {
if manager.any_failed_to_install {
Err(1)
} else {
Ok(())
}
}
Err(e) => {
eprintln!("bao install: {}", e);
Err(1)
}
}
}