Skip to main content

bun_runtime/
install.rs

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