Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions src/bunfig/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bun_bundler::options;
use bun_core::ZStr;
use bun_core::{self, Global, Output, env_var};
use bun_options_types::command_tag::{ALWAYS_LOADS_CONFIG, Tag as CommandTag};
use bun_options_types::context::Context;
use bun_options_types::context::{Context, ContextData};
use bun_paths::PathBuffer;
use bun_paths::resolve_path::{self, platform};
use bun_standalone_graph::StandaloneModuleGraph::StandaloneModuleGraph;
Expand All @@ -17,22 +17,24 @@ use crate::bunfig::Bunfig;

// ─── bunfig loading ──────────────────────────────────────────────────────────

fn get_home_config_path(buf: &mut PathBuffer) -> Option<&ZStr> {
let paths: [&[u8]; 1] = [b".bunfig.toml"];

if let Some(data_dir) = env_var::XDG_CONFIG_HOME.get() {
return Some(resolve_path::join_abs_string_buf_z::<platform::Auto>(
data_dir, &mut **buf, &paths,
));
}
/// `$XDG_CONFIG_HOME/.bunfig.toml`, else `$HOME/.bunfig.toml`; a relative directory is resolved against `cwd`.
fn get_home_config_path<'b>(cwd: &[u8], buf: &'b mut PathBuffer) -> Option<&'b ZStr> {
let config_dir = env_var::XDG_CONFIG_HOME
.get_not_empty()
.or_else(|| env_var::HOME.get_not_empty())?;
let parts: [&[u8]; 2] = [config_dir, b".bunfig.toml"];
let len = resolve_path::join_abs_string_buf_z::<platform::Auto>(cwd, &mut **buf, &parts).len();
Some(ZStr::from_buf(&buf[..], len))
}

if let Some(home_dir) = env_var::HOME.get() {
return Some(resolve_path::join_abs_string_buf_z::<platform::Auto>(
home_dir, &mut **buf, &paths,
));
/// Recorded by `Arguments::parse` (after `--cwd`); callers that skip it get the live cwd.
fn absolute_working_dir(ctx: &mut ContextData) -> Option<&[u8]> {
if ctx.args.absolute_working_dir.is_none() {
let mut buf = PathBuffer::uninit();
let len = bun_sys::getcwd(&mut *buf).ok()?;
ctx.args.absolute_working_dir = Some(Box::<[u8]>::from(&buf[..len]));
}

None
ctx.args.absolute_working_dir.as_deref()
}

fn load_bunfig(
Expand Down Expand Up @@ -87,7 +89,9 @@ fn load_global_bunfig(cmd: CommandTag, ctx: Context<'_>) -> Result<(), crate::Er
ctx.has_loaded_global_config = true;

let mut config_buf = PathBuffer::uninit();
if let Some(path) = get_home_config_path(&mut config_buf) {
if let Some(path) =
absolute_working_dir(ctx).and_then(|cwd| get_home_config_path(cwd, &mut config_buf))
{
load_bunfig(cmd, true, path, ctx)?;
}
Ok(())
Expand Down Expand Up @@ -156,7 +160,9 @@ pub fn load_config(
if !ctx.has_loaded_global_config {
ctx.has_loaded_global_config = true;

if let Some(path) = get_home_config_path(&mut config_buf) {
if let Some(path) =
absolute_working_dir(ctx).and_then(|cwd| get_home_config_path(cwd, &mut config_buf))
{
if let Err(err) = load_config_path(cmd, true, path, ctx) {
report_bunfig_load_failure(ctx.log, err);
}
Expand Down Expand Up @@ -193,22 +199,15 @@ pub fn load_config(
config_buf[config_path_.len()] = 0;
config_path_len = config_path_.len();
} else {
if ctx.args.absolute_working_dir.is_none() {
let mut secondbuf = PathBuffer::uninit();
let cwd_len = match bun_sys::getcwd(&mut *secondbuf) {
Ok(n) => n,
Err(_) => return Ok(()),
};
ctx.args.absolute_working_dir = Some(Box::<[u8]>::from(&secondbuf[..cwd_len]));
}

let Some(awd) = absolute_working_dir(ctx) else {
return Ok(());
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Reshaped for borrowck: `join_abs_string_buf` ties the
// returned slice's lifetime to both `cwd` (borrowed from `ctx.args`)
// returned slice's lifetime to both `awd` (borrowed from `ctx.args`)
// and `config_buf`. We only need the length to NUL-terminate and
// re-wrap, so capture `joined.len()` and drop the `ctx` borrow before
// the `&mut ctx` call below.
config_path_len = {
let awd: &[u8] = ctx.args.absolute_working_dir.as_deref().unwrap();
let parts: [&[u8]; 2] = [awd, config_path_];
let joined =
resolve_path::join_abs_string_buf::<platform::Auto>(awd, &mut *config_buf, &parts);
Expand Down
13 changes: 10 additions & 3 deletions src/install/PackageManager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1921,22 +1921,29 @@ pub fn init(
let npmrc_local = ZBox::from_bytes(b".npmrc");

let mut buf = PathBuffer::uninit();
let parts = [b"./.npmrc" as &[u8]];
// The process has since changed into the workspace root or, for `-g`, the global directory.
let started_in: &[u8] = ctx
.args
.absolute_working_dir
.as_deref()
.unwrap_or(&original_cwd_clone);

// npm reads `$HOME/.npmrc` and ignores XDG_CONFIG_HOME; keep
// `$XDG_CONFIG_HOME/.npmrc` only when that file actually exists.
let mut global_len: usize = 0;
if let Some(xdg_dir) = bun_core::env_var::XDG_CONFIG_HOME.get_not_empty() {
let parts: [&[u8]; 2] = [xdg_dir, b".npmrc"];
let p =
resolve_path::join_abs_string_buf_z::<platform::Auto>(xdg_dir, &mut buf, &parts);
resolve_path::join_abs_string_buf_z::<platform::Auto>(started_in, &mut buf, &parts);
if bun_sys::exists_z(p) {
global_len = p.len();
}
}
if global_len == 0 {
if let Some(home_dir) = bun_core::env_var::HOME.get_not_empty() {
let parts: [&[u8]; 2] = [home_dir, b".npmrc"];
global_len = resolve_path::join_abs_string_buf_z::<platform::Auto>(
home_dir, &mut buf, &parts,
started_in, &mut buf, &parts,
)
.len();
}
Expand Down
44 changes: 6 additions & 38 deletions src/install/PackageManager/PackageManagerDirectories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::bun_fs::FileSystem;
use crate::lockfile_real::package::PackageColumns;
use crate::repository::Repository;
use bun_core::ZStr;
use bun_core::{Global, Output, ZBox, env_var, fmt as bun_fmt};
use bun_core::{Global, Output, ZBox, fmt as bun_fmt};
use bun_dotenv::Loader as DotEnvLoader;
use bun_install::lockfile::{Format as LockfileFormat, LoadResult, Lockfile};
use bun_install::resolution::Tag as ResolutionTag;
Expand Down Expand Up @@ -376,44 +376,12 @@ pub struct CacheDir {
}

pub fn fetch_cache_directory_path(env: &mut DotEnvLoader, options: Option<&Options>) -> CacheDir {
if let Some(dir) = env.get(b"BUN_INSTALL_CACHE_DIR") {
return CacheDir {
path: FileSystem::instance().abs(&[dir]).to_vec(),
};
}

if let Some(opts) = options {
if !opts.cache_directory.is_empty() {
return CacheDir {
path: FileSystem::instance().abs(&[opts.cache_directory]).to_vec(),
};
}
}

if let Some(dir) = env.get(b"BUN_INSTALL") {
let parts: [&[u8]; 3] = [dir, b"install/", b"cache/"];
return CacheDir {
path: FileSystem::instance().abs(&parts).to_vec(),
};
}

if let Some(dir) = env_var::XDG_CACHE_HOME.get() {
let parts: [&[u8]; 4] = [dir, b".bun/", b"install/", b"cache/"];
return CacheDir {
path: FileSystem::instance().abs(&parts).to_vec(),
};
}

if let Some(dir) = env_var::HOME.get() {
let parts: [&[u8]; 4] = [dir, b".bun/", b"install/", b"cache/"];
return CacheDir {
path: FileSystem::instance().abs(&parts).to_vec(),
};
}

let fallback_parts: [&[u8]; 1] = [b"node_modules/.bun-cache"];
CacheDir {
path: FileSystem::instance().abs(&fallback_parts).to_vec(),
path: bun_options_types::install_cache_dir::fetch_cache_directory_path(
FileSystem::instance().top_level_dir(),
env,
options.map(|opts| opts.cache_directory),
),
}
}

Expand Down
98 changes: 48 additions & 50 deletions src/install/PackageManager/PackageManagerOptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,51 @@ pub use crate::config_version::ConfigVersion;
pub use bun_install_types::DependencyGroup;
pub use bun_install_types::NodeLinker::NodeLinker;

/// Resolved once: `-g` changes into the global directory between opening it and opening the bin directory.
fn global_install_root() -> Option<&'static [u8]> {
static ROOT: bun_core::Once<Option<Box<[u8]>>> = bun_core::Once::new();
ROOT.get_or_init(|| {
use bun_paths::{platform, resolve_path};

let mut cwd_buf = bun_paths::path_buffer_pool::get();
let cwd = resolve_path::working_dir(&mut cwd_buf);
if let Some(dir) = env_var::BUN_INSTALL.get_not_empty() {
return Some(Box::from(resolve_path::join_abs_string::<platform::Auto>(
cwd,
&[dir],
)));
}
let home_dir = env_var::XDG_CACHE_HOME
.get_not_empty()
.or_else(|| env_var::HOME.get_not_empty())?;
let parts: [&[u8]; 2] = [home_dir, b".bun"];
Some(Box::from(resolve_path::join_abs_string::<platform::Auto>(
cwd, &parts,
)))
})
.as_deref()
}
Comment thread
robobun marked this conversation as resolved.

fn make_open_global_path(root: &[u8], parts: &[&[u8]]) -> crate::Result<bun_sys::Fd> {
use bun_paths::{platform, resolve_path::join_abs_string_buf};
use bun_sys::{Dir, OpenDirOptions};

let mut buf = PathBuffer::uninit();
let path = join_abs_string_buf::<platform::Auto>(root, &mut buf.0, parts);
Dir::cwd()
.make_open_path(path, OpenDirOptions::default())
.map(|d| d.into_raw())
.map_err(Into::into)
}

// mkdir -p + open the dir. Callers store the raw `Fd` (`options.global_bin_dir: Fd`).
pub fn open_global_dir(explicit_global_dir: &[u8]) -> crate::Result<bun_sys::Fd> {
use bun_paths::{platform, resolve_path::join_abs_string_buf};
use bun_sys::{Dir, OpenDirOptions};

if let Some(home_dir) = env_var::BUN_INSTALL_GLOBAL_DIR.get() {
// Resolved here even when an explicit directory wins below: `open_global_bin_dir` runs after the `-g` chdir.
let root = global_install_root();

if let Some(home_dir) = env_var::BUN_INSTALL_GLOBAL_DIR.get_not_empty() {
return Dir::cwd()
.make_open_path(home_dir, OpenDirOptions::default())
.map(|d| d.into_raw())
Expand All @@ -317,37 +356,16 @@ pub fn open_global_dir(explicit_global_dir: &[u8]) -> crate::Result<bun_sys::Fd>
.map_err(Into::into);
}

if let Some(home_dir) = env_var::BUN_INSTALL.get() {
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 2] = [b"install", b"global"];
let path = join_abs_string_buf::<platform::Auto>(home_dir, &mut buf.0, &parts);
return Dir::cwd()
.make_open_path(path, OpenDirOptions::default())
.map(|d| d.into_raw())
.map_err(Into::into);
match root {
Some(root) => make_open_global_path(root, &[b"install", b"global"]),
None => Err(crate::Error::NoGlobalDirectoryFound),
}

if let Some(home_dir) = env_var::XDG_CACHE_HOME
.get()
.or_else(|| env_var::HOME.get())
{
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 3] = [b".bun", b"install", b"global"];
let path = join_abs_string_buf::<platform::Auto>(home_dir, &mut buf.0, &parts);
return Dir::cwd()
.make_open_path(path, OpenDirOptions::default())
.map(|d| d.into_raw())
.map_err(Into::into);
}

Err(crate::Error::NoGlobalDirectoryFound)
}

pub(crate) fn open_global_bin_dir(opts_: Option<&Api::BunInstall>) -> crate::Result<bun_sys::Fd> {
use bun_paths::{platform, resolve_path::join_abs_string_buf};
use bun_sys::{Dir, OpenDirOptions};

if let Some(home_dir) = env_var::BUN_INSTALL_BIN.get() {
if let Some(home_dir) = env_var::BUN_INSTALL_BIN.get_not_empty() {
return Dir::cwd()
.make_open_path(home_dir, OpenDirOptions::default())
.map(|d| d.into_raw())
Expand All @@ -365,30 +383,10 @@ pub(crate) fn open_global_bin_dir(opts_: Option<&Api::BunInstall>) -> crate::Res
}
}

if let Some(home_dir) = env_var::BUN_INSTALL.get() {
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 1] = [b"bin"];
let path = join_abs_string_buf::<platform::Auto>(home_dir, &mut buf.0, &parts);
return Dir::cwd()
.make_open_path(path, OpenDirOptions::default())
.map(|d| d.into_raw())
.map_err(Into::into);
}

if let Some(home_dir) = env_var::XDG_CACHE_HOME
.get()
.or_else(|| env_var::HOME.get())
{
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 2] = [b".bun", b"bin"];
let path = join_abs_string_buf::<platform::Auto>(home_dir, &mut buf.0, &parts);
return Dir::cwd()
.make_open_path(path, OpenDirOptions::default())
.map(|d| d.into_raw())
.map_err(Into::into);
match global_install_root() {
Some(root) => make_open_global_path(root, &[b"bin"]),
None => Err(crate::Error::MissingGlobalBinDirectoryTrySettingBUNINSTALL),
}

Err(crate::Error::MissingGlobalBinDirectoryTrySettingBUNINSTALL)
}

// `BunInstall` owns `Box<[u8]>`; Options stores `&'static [u8]`
Expand Down
12 changes: 8 additions & 4 deletions src/install/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,18 @@ impl SloppyGlobalGitConfig {
}

fn load_and_parse() -> SloppyGlobalGitConfig {
let Some(home_dir) = bun_core::env_var::HOME.get() else {
let Some(home_dir) = bun_core::env_var::HOME.get_not_empty() else {
return SloppyGlobalGitConfig::default();
};

let mut config_file_path_buf = PathBuffer::uninit();
let config_file_path = bun_paths::resolve_path::join_abs_string_buf_z::<
bun_paths::platform::Auto,
>(home_dir, &mut config_file_path_buf, &[b".gitconfig"]);
let parts: [&[u8]; 2] = [home_dir, b".gitconfig"];
let config_file_path =
bun_paths::resolve_path::join_abs_string_buf_z::<bun_paths::platform::Auto>(
bun_resolver::fs::FileSystem::get().top_level_dir(),
&mut config_file_path_buf,
&parts,
);
// MOVE_DOWN: `File::toSource` lives in `bun_logger` (T1→T2 cyclebreak).
let Ok(source) = bun_ast::to_source(
config_file_path,
Expand Down
9 changes: 5 additions & 4 deletions src/options_types/compile_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl CompileTarget {
&self,
buf: &'a mut PathBuffer,
version_str: &'a ZStr,
_env: &mut bun_dotenv::Loader,
env: &bun_dotenv::Loader,
needs_download: &mut bool,
) -> &'a ZStr {
if self.is_default() {
Expand All @@ -206,10 +206,11 @@ impl CompileTarget {
return version_str;
}

// T1 fallback ignores `_env` (full env-override chain lives in bun_install).
let cache_dir = bun_sys::fetch_cache_directory_path();
let top_level_dir = path::fs::FileSystem::instance().top_level_dir();
let cache_dir =
crate::install_cache_dir::fetch_cache_directory_path(top_level_dir, env, None);
let dest = path::resolve_path::join_abs_string_buf_z::<path::platform::Auto>(
path::fs::FileSystem::instance().top_level_dir(),
top_level_dir,
&mut buf[..],
&[cache_dir.as_slice(), version_str.as_bytes()],
);
Expand Down
Loading
Loading