diff --git a/src/StandaloneModuleGraph.zig b/src/StandaloneModuleGraph.zig index 72b7b38cff..49c659e0dd 100644 --- a/src/StandaloneModuleGraph.zig +++ b/src/StandaloneModuleGraph.zig @@ -432,7 +432,7 @@ pub const StandaloneModuleGraph = struct { }; if (comptime bun.Environment.is_canary or bun.Environment.isDebug) { - if (bun.getenvZ("BUN_FEATURE_FLAG_DUMP_CODE")) |dump_code_dir| { + if (bun.env_var.BUN_FEATURE_FLAG_DUMP_CODE.get()) |dump_code_dir| { const buf = bun.path_buffer_pool.get(); defer bun.path_buffer_pool.put(buf); const dest_z = bun.path.joinAbsStringBufZ(dump_code_dir, buf, &.{dest_path}, .auto); @@ -1328,7 +1328,7 @@ pub const StandaloneModuleGraph = struct { var whichbuf: bun.PathBuffer = undefined; if (bun.which( &whichbuf, - bun.getenvZ("PATH") orelse return error.FileNotFound, + bun.env_var.PATH.get() orelse return error.FileNotFound, "", bun.argv[0], )) |path| { diff --git a/src/analytics.zig b/src/analytics.zig index a536511be4..0ac7156967 100644 --- a/src/analytics.zig +++ b/src/analytics.zig @@ -12,12 +12,10 @@ pub fn isEnabled() bool { .no => false, .unknown => { enabled = detect: { - if (bun.getenvZ("DO_NOT_TRACK")) |x| { - if (x.len == 1 and x[0] == '1') { - break :detect .no; - } + if (bun.env_var.DO_NOT_TRACK.get()) { + break :detect .no; } - if (bun.getenvZ("HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET") != null) { + if (bun.env_var.HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET.get() != null) { break :detect .no; } break :detect .yes; diff --git a/src/bake.zig b/src/bake.zig index d240ece752..49b045ef60 100644 --- a/src/bake.zig +++ b/src/bake.zig @@ -984,7 +984,7 @@ pub const PatternBuffer = struct { pub fn printWarning() void { // Silence this for the test suite - if (bun.getenvZ("BUN_DEV_SERVER_TEST_RUNNER") == null) { + if (bun.env_var.BUN_DEV_SERVER_TEST_RUNNER.get() == null) { bun.Output.warn( \\Be advised that Bun Bake is highly experimental, and its API \\will have breaking changes. Join the #bake Discord diff --git a/src/bake/DevServer.zig b/src/bake/DevServer.zig index e14cc0564c..e85e874890 100644 --- a/src/bake/DevServer.zig +++ b/src/bake/DevServer.zig @@ -318,7 +318,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { .memory_visualizer_timer = .initPaused(.DevServerMemoryVisualizerTick), .has_pre_crash_handler = bun.FeatureFlags.bake_debugging_features and options.dump_state_on_crash orelse - bun.getRuntimeFeatureFlag(.BUN_DUMP_STATE_ON_CRASH), + bun.feature_flag.BUN_DUMP_STATE_ON_CRASH.get(), .frontend_only = options.framework.file_system_router_types.len == 0, .client_graph = .empty, .server_graph = .empty, @@ -343,13 +343,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { .source_maps = .empty, .plugin_state = .unknown, .bundling_failures = .{}, - .assume_perfect_incremental_bundling = if (bun.Environment.isDebug) - if (bun.getenvZ("BUN_ASSUME_PERFECT_INCREMENTAL")) |env| - !bun.strings.eqlComptime(env, "0") - else - true - else - bun.getRuntimeFeatureFlag(.BUN_ASSUME_PERFECT_INCREMENTAL), + .assume_perfect_incremental_bundling = bun.feature_flag.BUN_ASSUME_PERFECT_INCREMENTAL.get() orelse bun.Environment.isDebug, .testing_batch_events = .disabled, .broadcast_console_log_from_browser_to_server = options.broadcast_console_log_from_browser_to_server, .server_transpiler = undefined, diff --git a/src/bun.js/ModuleLoader.zig b/src/bun.js/ModuleLoader.zig index 90e21151ca..d1ce74545a 100644 --- a/src/bun.js/ModuleLoader.zig +++ b/src/bun.js/ModuleLoader.zig @@ -2037,7 +2037,7 @@ fn dumpSourceString(vm: *VirtualMachine, specifier: string, written: []const u8) fn dumpSourceStringFailiable(vm: *VirtualMachine, specifier: string, written: []const u8) !void { if (!Environment.isDebug) return; - if (bun.getRuntimeFeatureFlag(.BUN_DEBUG_NO_DUMP)) return; + if (bun.feature_flag.BUN_DEBUG_NO_DUMP.get()) return; const BunDebugHolder = struct { pub var dir: ?std.fs.Dir = null; diff --git a/src/bun.js/RuntimeTranspilerCache.zig b/src/bun.js/RuntimeTranspilerCache.zig index d6d3c8842b..50b05809b3 100644 --- a/src/bun.js/RuntimeTranspilerCache.zig +++ b/src/bun.js/RuntimeTranspilerCache.zig @@ -383,10 +383,10 @@ pub const RuntimeTranspilerCache = struct { fn reallyGetCacheDir(buf: *bun.PathBuffer) [:0]const u8 { if (comptime bun.Environment.isDebug) { - bun_debug_restore_from_cache = bun.getenvZ("BUN_DEBUG_ENABLE_RESTORE_FROM_TRANSPILER_CACHE") != null; + bun_debug_restore_from_cache = bun.env_var.BUN_DEBUG_ENABLE_RESTORE_FROM_TRANSPILER_CACHE.get(); } - if (bun.getenvZ("BUN_RUNTIME_TRANSPILER_CACHE_PATH")) |dir| { + if (bun.env_var.BUN_RUNTIME_TRANSPILER_CACHE_PATH.get()) |dir| { if (dir.len == 0 or (dir.len == 1 and dir[0] == '0')) { return ""; } @@ -397,7 +397,7 @@ pub const RuntimeTranspilerCache = struct { return buf[0..len :0]; } - if (bun.getenvZ("XDG_CACHE_HOME")) |dir| { + if (bun.env_var.XDG_CACHE_HOME.get()) |dir| { const parts = &[_][]const u8{ dir, "bun", "@t@" }; return bun.fs.FileSystem.instance.absBufZ(parts, buf); } @@ -405,7 +405,7 @@ pub const RuntimeTranspilerCache = struct { if (comptime bun.Environment.isMac) { // On a mac, default to ~/Library/Caches/bun/* // This is different than ~/.bun/install/cache, and not configurable by the user. - if (bun.getenvZ("HOME")) |home| { + if (bun.env_var.HOME.get()) |home| { const parts = &[_][]const u8{ home, "Library/", @@ -417,7 +417,7 @@ pub const RuntimeTranspilerCache = struct { } } - if (bun.getenvZ(bun.DotEnv.home_env)) |dir| { + if (bun.env_var.HOME.get()) |dir| { const parts = &[_][]const u8{ dir, ".bun", "install", "cache", "@t@" }; return bun.fs.FileSystem.instance.absBufZ(parts, buf); } diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index ea0095dfea..61f3fa5ae6 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -220,7 +220,7 @@ pub fn initRequestBodyValue(this: *VirtualMachine, body: jsc.WebCore.Body.Value) /// Worker VMs are always destroyed on exit, regardless of this setting. Setting this to /// true may expose bugs that would otherwise only occur using Workers. Controlled by pub fn shouldDestructMainThreadOnExit(_: *const VirtualMachine) bool { - return bun.getRuntimeFeatureFlag(.BUN_DESTRUCT_VM_ON_EXIT); + return bun.feature_flag.BUN_DESTRUCT_VM_ON_EXIT.get(); } pub threadlocal var is_bundler_thread_for_bytecode_cache: bool = false; @@ -464,7 +464,7 @@ pub fn loadExtraEnvAndSourceCodePrinter(this: *VirtualMachine) void { this.hide_bun_stackframes = false; } - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER.get()) { this.transpiler_store.enabled = false; } @@ -1199,12 +1199,12 @@ pub inline fn assertOnJSThread(vm: *const VirtualMachine) void { } fn configureDebugger(this: *VirtualMachine, cli_flag: bun.cli.Command.Debugger) void { - if (bun.getenvZ("HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET") != null) { + if (bun.env_var.HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET.get() != null) { return; } - const unix = bun.getenvZ("BUN_INSPECT") orelse ""; - const connect_to = bun.getenvZ("BUN_INSPECT_CONNECT_TO") orelse ""; + const unix = bun.env_var.BUN_INSPECT.get(); + const connect_to = bun.env_var.BUN_INSPECT_CONNECT_TO.get(); const set_breakpoint_on_first_line = unix.len > 0 and strings.endsWith(unix, "?break=1"); // If we should set a breakpoint on the first line const wait_for_debugger = unix.len > 0 and strings.endsWith(unix, "?wait=1"); // If we should wait for the debugger to connect before starting the event loop @@ -2648,8 +2648,8 @@ pub fn remapZigException( ) void { error_instance.toZigException(this.global, exception); const enable_source_code_preview = allow_source_code_preview and - !(bun.getRuntimeFeatureFlag(.BUN_DISABLE_SOURCE_CODE_PREVIEW) or - bun.getRuntimeFeatureFlag(.BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW)); + !(bun.feature_flag.BUN_DISABLE_SOURCE_CODE_PREVIEW.get() or + bun.feature_flag.BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW.get()); defer { if (Environment.isDebug) { @@ -3348,7 +3348,7 @@ pub noinline fn printGithubAnnotation(exception: *ZigException) void { const message = exception.message; const frames = exception.stack.frames(); const top_frame = if (frames.len > 0) frames[0] else null; - const dir = bun.getenvZ("GITHUB_WORKSPACE") orelse bun.fs.FileSystem.instance.top_level_dir; + const dir = bun.env_var.GITHUB_WORKSPACE.get() orelse bun.fs.FileSystem.instance.top_level_dir; const allocator = bun.default_allocator; Output.flush(); diff --git a/src/bun.js/api/bun/dns.zig b/src/bun.js/api/bun/dns.zig index 50256ff78f..39dc1bb30e 100644 --- a/src/bun.js/api/bun/dns.zig +++ b/src/bun.js/api/bun/dns.zig @@ -1147,26 +1147,11 @@ pub const internal = struct { var __max_dns_time_to_live_seconds: ?u32 = null; pub fn getMaxDNSTimeToLiveSeconds() u32 { - // Amazon Web Services recommends 5 seconds: https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/jvm-ttl-dns.html - const default_max_dns_time_to_live_seconds = 30; - // This is racy, but it's okay because the number won't be invalid, just stale. return __max_dns_time_to_live_seconds orelse { - if (bun.getenvZ("BUN_CONFIG_DNS_TIME_TO_LIVE_SECONDS")) |string_value| { - const value = std.fmt.parseInt(i64, string_value, 10) catch { - __max_dns_time_to_live_seconds = default_max_dns_time_to_live_seconds; - return default_max_dns_time_to_live_seconds; - }; - if (value < 0) { - __max_dns_time_to_live_seconds = std.math.maxInt(u32); - } else { - __max_dns_time_to_live_seconds = @truncate(@as(u64, @intCast(value))); - } - return __max_dns_time_to_live_seconds.?; - } - - __max_dns_time_to_live_seconds = default_max_dns_time_to_live_seconds; - return default_max_dns_time_to_live_seconds; + const value = bun.env_var.BUN_CONFIG_DNS_TIME_TO_LIVE_SECONDS.get(); + __max_dns_time_to_live_seconds = @truncate(@as(u64, @intCast(value))); + return __max_dns_time_to_live_seconds.?; }; } @@ -1393,12 +1378,12 @@ pub const internal = struct { }; pub fn getHints() std.c.addrinfo { var hints_copy = default_hints; - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG.get()) { hints_copy.flags.ADDRCONFIG = false; } - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_IPV6)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_IPV6.get()) { hints_copy.family = std.c.AF.INET; - } else if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_IPV4)) { + } else if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_IPV4.get()) { hints_copy.family = std.c.AF.INET6; } @@ -1685,7 +1670,7 @@ pub const internal = struct { getaddrinfo_calls += 1; var timestamp_to_store: u32 = 0; // is there a cache hit? - if (!bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_DNS_CACHE)) { + if (!bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_DNS_CACHE.get()) { if (global_cache.get(key, ×tamp_to_store)) |entry| { if (preload) { global_cache.lock.unlock(); @@ -1724,7 +1709,7 @@ pub const internal = struct { global_cache.lock.unlock(); if (comptime Environment.isMac) { - if (!bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO)) { + if (!bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO.get()) { const res = lookupLibinfo(req, loop.internal_loop_data.getParent()); log("getaddrinfo({s}) = cache miss (libinfo)", .{host orelse ""}); if (res) return req; diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index c07203e565..c0f0024e06 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -1351,7 +1351,7 @@ pub fn spawnMaybeSync( !jsc_vm.auto_killer.enabled and !jsc_vm.jsc_vm.hasExecutionTimeLimit() and !jsc_vm.isInspectorEnabled() and - !bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH); + !bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH.get(); const spawn_options = bun.spawn.SpawnOptions{ .cwd = cwd, diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 8325117440..ff84db1694 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -321,7 +321,7 @@ pub const FFI = struct { pub fn compile(this: *CompileC, globalThis: *JSGlobalObject) !struct { *TCC.State, []u8 } { const compile_options: [:0]const u8 = if (this.flags.len > 0) this.flags - else if (bun.getenvZ("BUN_TCC_OPTIONS")) |tcc_options| + else if (bun.env_var.BUN_TCC_OPTIONS.get()) |tcc_options| @ptrCast(tcc_options) else default_tcc_options; @@ -349,7 +349,7 @@ pub const FFI = struct { if (Environment.isMac) { add_system_include_dir: { const dirs_to_try = [_][]const u8{ - bun.getenvZ("SDKROOT") orelse "", + bun.env_var.SDKROOT.get() orelse "", getSystemIncludeDir() orelse "", }; diff --git a/src/bun.js/event_loop/GarbageCollectionController.zig b/src/bun.js/event_loop/GarbageCollectionController.zig index 7b2088f93b..2a13be5a9b 100644 --- a/src/bun.js/event_loop/GarbageCollectionController.zig +++ b/src/bun.js/event_loop/GarbageCollectionController.zig @@ -37,7 +37,7 @@ pub fn init(this: *GarbageCollectionController, vm: *VirtualMachine) void { actual.internal_loop_data.jsc_vm = vm.jsc_vm; if (comptime Environment.isDebug) { - if (bun.getenvZ("BUN_TRACK_LAST_FN_NAME") != null) { + if (bun.env_var.BUN_TRACK_LAST_FN_NAME.get()) { vm.eventLoop().debug.track_last_fn_name = true; } } diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index 186e60377e..f9426e7ecc 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -314,7 +314,7 @@ pub fn homedir(global: *jsc.JSGlobalObject) !bun.String { // The posix implementation of uv_os_homedir first checks the HOME // environment variable, then falls back to reading the passwd entry. - if (bun.getenvZ("HOME")) |home| { + if (bun.env_var.HOME.get()) |home| { if (home.len > 0) return bun.String.init(home); } @@ -938,15 +938,15 @@ pub fn userInfo(globalThis: *jsc.JSGlobalObject, options: gen.UserInfoOptions) b result.put(globalThis, jsc.ZigString.static("homedir"), home.toJS(globalThis)); if (comptime Environment.isWindows) { - result.put(globalThis, jsc.ZigString.static("username"), jsc.ZigString.init(bun.getenvZ("USERNAME") orelse "unknown").withEncoding().toJS(globalThis)); + result.put(globalThis, jsc.ZigString.static("username"), jsc.ZigString.init(bun.env_var.USER.get() orelse "unknown").withEncoding().toJS(globalThis)); result.put(globalThis, jsc.ZigString.static("uid"), jsc.JSValue.jsNumber(-1)); result.put(globalThis, jsc.ZigString.static("gid"), jsc.JSValue.jsNumber(-1)); result.put(globalThis, jsc.ZigString.static("shell"), jsc.JSValue.jsNull()); } else { - const username = bun.getenvZ("USER") orelse "unknown"; + const username = bun.env_var.USER.get() orelse "unknown"; result.put(globalThis, jsc.ZigString.static("username"), jsc.ZigString.init(username).withEncoding().toJS(globalThis)); - result.put(globalThis, jsc.ZigString.static("shell"), jsc.ZigString.init(bun.getenvZ("SHELL") orelse "unknown").withEncoding().toJS(globalThis)); + result.put(globalThis, jsc.ZigString.static("shell"), jsc.ZigString.init(bun.env_var.SHELL.get() orelse "unknown").withEncoding().toJS(globalThis)); result.put(globalThis, jsc.ZigString.static("uid"), jsc.JSValue.jsNumber(c.getuid())); result.put(globalThis, jsc.ZigString.static("gid"), jsc.JSValue.jsNumber(c.getgid())); } diff --git a/src/bun.js/node/node_process.zig b/src/bun.js/node/node_process.zig index d8172ec238..91362e45f0 100644 --- a/src/bun.js/node/node_process.zig +++ b/src/bun.js/node/node_process.zig @@ -339,11 +339,11 @@ comptime { } pub export fn Bun__NODE_NO_WARNINGS() bool { - return bun.getRuntimeFeatureFlag(.NODE_NO_WARNINGS); + return bun.feature_flag.NODE_NO_WARNINGS.get(); } pub export fn Bun__suppressCrashOnProcessKillSelfIfDesired() void { - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_ON_PROCESS_KILL_SELF)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_ON_PROCESS_KILL_SELF.get()) { bun.crash_handler.suppressReporting(); } } diff --git a/src/bun.js/test/debug.zig b/src/bun.js/test/debug.zig index 3b56f5fda9..7933ab53f9 100644 --- a/src/bun.js/test/debug.zig +++ b/src/bun.js/test/debug.zig @@ -52,16 +52,8 @@ pub const group = struct { } var indent: usize = 0; var last_was_start = false; - var wants_quiet: ?bool = null; fn getLogEnabledRuntime() bool { - if (wants_quiet) |v| return !v; - if (bun.getenvZ("WANTS_LOUD")) |val| { - const loud = !std.mem.eql(u8, val, "0"); - wants_quiet = !loud; - return loud; - } - wants_quiet = true; // default quiet - return false; + return bun.env_var.WANTS_LOUD.get(); } inline fn getLogEnabledStaticFalse() bool { return false; diff --git a/src/bun.js/webcore/blob/copy_file.zig b/src/bun.js/webcore/blob/copy_file.zig index 8b045280b9..7868e277cd 100644 --- a/src/bun.js/webcore/blob/copy_file.zig +++ b/src/bun.js/webcore/blob/copy_file.zig @@ -881,7 +881,7 @@ pub const CopyFileWindows = struct { fn copyfile(this: *CopyFileWindows) void { // This is for making it easier for us to test this code path - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE.get()) { this.prepareReadWriteLoop(); return; } diff --git a/src/bun.zig b/src/bun.zig index e7c09f62dd..f1f13acbef 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -7,6 +7,8 @@ const bun = @This(); pub const Environment = @import("./env.zig"); +pub const env_var = @import("./env_var.zig"); +pub const feature_flag = env_var.feature_flag; pub const use_mimalloc = @import("build_options").use_mimalloc; pub const default_allocator: std.mem.Allocator = allocators.c_allocator; @@ -494,11 +496,10 @@ pub fn fastRandom() u64 { // and we only need to do it once per process var value = seed_value.load(.monotonic); while (value == 0) : (value = seed_value.load(.monotonic)) { - if (comptime Environment.isDebug or Environment.is_canary) outer: { - if (getenvZ("BUN_DEBUG_HASH_RANDOM_SEED")) |env| { - value = std.fmt.parseInt(u64, env, 10) catch break :outer; - seed_value.store(value, .monotonic); - return value; + if (comptime Environment.isDebug or Environment.is_canary) { + if (bun.env_var.BUN_DEBUG_HASH_RANDOM_SEED.get()) |v| { + seed_value.store(v, .monotonic); + return v; } } csprng(std.mem.asBytes(&value)); @@ -820,31 +821,11 @@ pub fn openDirAbsoluteNotForDeletingOrRenaming(path_: []const u8) !std.fs.Dir { return fd.stdDir(); } -pub fn getRuntimeFeatureFlag(comptime flag: FeatureFlags.RuntimeFeatureFlag) bool { - return struct { - const state = enum(u8) { idk, disabled, enabled }; - var is_enabled: std.atomic.Value(state) = std.atomic.Value(state).init(.idk); - pub fn get() bool { - // .monotonic is okay because there are no side effects we need to observe from a thread that has - // written to this variable. This variable is simply a cache, and if its value is not ready yet, we - // compute it below. There are no correctness issues if multiple threads perform this computation - // simultaneously, as they will all store the same value. - return switch (is_enabled.load(.monotonic)) { - .enabled => true, - .disabled => false, - .idk => { - const enabled = if (getenvZ(@tagName(flag))) |val| - strings.eqlComptime(val, "1") or strings.eqlComptime(val, "true") - else - false; - is_enabled.store(if (enabled) .enabled else .disabled, .monotonic); - return enabled; - }, - }; - } - }.get(); -} - +/// Note: You likely do not need this function. See the pattern in env_var.zig for adding +/// environment variables. +/// TODO(markovejnovic): Sunset this function when its last usage is removed. +/// This wrapper exists to avoid the call to sliceTo(0) +/// Zig's sliceTo(0) is scalar pub fn getenvZAnyCase(key: [:0]const u8) ?[]const u8 { for (std.os.environ) |lineZ| { const line = sliceTo(lineZ, 0); @@ -857,6 +838,9 @@ pub fn getenvZAnyCase(key: [:0]const u8) ?[]const u8 { return null; } +/// Note: You likely do not need this function. See the pattern in env_var.zig for adding +/// environment variables. +/// TODO(markovejnovic): Sunset this function when its last usage is removed. /// This wrapper exists to avoid the call to sliceTo(0) /// Zig's sliceTo(0) is scalar pub fn getenvZ(key: [:0]const u8) ?[]const u8 { @@ -872,6 +856,9 @@ pub fn getenvZ(key: [:0]const u8) ?[]const u8 { return sliceTo(pointer, 0); } +/// Note: You likely do not need this function. See the pattern in env_var.zig for adding +/// environment variables. +/// TODO(markovejnovic): Sunset this function when its last usage is removed. pub fn getenvTruthy(key: [:0]const u8) bool { if (getenvZ(key)) |value| return std.mem.eql(u8, value, "true") or std.mem.eql(u8, value, "1"); return false; @@ -1330,7 +1317,7 @@ pub fn getFdPath(fd: FileDescriptor, buf: *bun.PathBuffer) ![]u8 { if (!ProcSelfWorkAroundForDebugging.has_checked) { ProcSelfWorkAroundForDebugging.has_checked = true; - needs_proc_self_workaround = strings.eql(getenvZ("BUN_NEEDS_PROC_SELF_WORKAROUND") orelse "0", "1"); + needs_proc_self_workaround = bun.env_var.BUN_NEEDS_PROC_SELF_WORKAROUND.get(); } } else if (comptime !Environment.isLinux) { return try std.os.getFdPath(fd.native(), buf); @@ -2221,7 +2208,7 @@ pub fn initArgv(allocator: std.mem.Allocator) !void { argv = try std.process.argsAlloc(allocator); } - if (bun.getenvZ("BUN_OPTIONS")) |opts| { + if (bun.env_var.BUN_OPTIONS.get()) |opts| { var argv_list = std.ArrayList([:0]const u8).fromOwnedSlice(allocator, argv); try appendOptionsEnv(opts, &argv_list, allocator); argv = argv_list.items; diff --git a/src/bundler/ThreadPool.zig b/src/bundler/ThreadPool.zig index fb4a8c1db7..f1f260a279 100644 --- a/src/bundler/ThreadPool.zig +++ b/src/bundler/ThreadPool.zig @@ -118,12 +118,12 @@ pub const ThreadPool = struct { } pub fn usesIOPool() bool { - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_FORCE_IO_POOL)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_FORCE_IO_POOL.get()) { // For testing. return true; } - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_IO_POOL)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_IO_POOL.get()) { // For testing. return false; } diff --git a/src/bundler/linker_context/StaticRouteVisitor.zig b/src/bundler/linker_context/StaticRouteVisitor.zig index fbade1b0ff..ba06fb00ef 100644 --- a/src/bundler/linker_context/StaticRouteVisitor.zig +++ b/src/bundler/linker_context/StaticRouteVisitor.zig @@ -18,7 +18,7 @@ pub fn deinit(this: *StaticRouteVisitor) void { /// Investigate performance. It can have false negatives (it doesn't properly /// handle cycles), but that's okay as it's just used an optimization pub fn hasTransitiveUseClient(this: *StaticRouteVisitor, entry_point_source_index: u32) bool { - if (bun.Environment.isDebug and bun.getenvZ("BUN_SSG_DISABLE_STATIC_ROUTE_VISITOR") != null) { + if (bun.Environment.isDebug and bun.env_var.BUN_SSG_DISABLE_STATIC_ROUTE_VISITOR.get()) { return false; } diff --git a/src/bundler/linker_context/findImportedFilesInCSSOrder.zig b/src/bundler/linker_context/findImportedFilesInCSSOrder.zig index 2384be6932..74e5c7fb5c 100644 --- a/src/bundler/linker_context/findImportedFilesInCSSOrder.zig +++ b/src/bundler/linker_context/findImportedFilesInCSSOrder.zig @@ -604,7 +604,7 @@ const CssOrderDebugStep = enum { fn debugCssOrder(this: *LinkerContext, order: *const BabyList(Chunk.CssImportOrder), comptime step: CssOrderDebugStep) void { if (comptime bun.Environment.isDebug) { const env_var = "BUN_DEBUG_CSS_ORDER_" ++ @tagName(step); - const enable_all = bun.getenvTruthy("BUN_DEBUG_CSS_ORDER"); + const enable_all = bun.env_var.BUN_DEBUG_CSS_ORDER.get(); if (enable_all or bun.getenvTruthy(env_var)) { debugCssOrderImpl(this, order, step); } diff --git a/src/ci_info.zig b/src/ci_info.zig index 861fa30845..00a1f7099a 100644 --- a/src/ci_info.zig +++ b/src/ci_info.zig @@ -73,14 +73,14 @@ const CI = enum { var name: []const u8 = ""; defer ci_name = name; - if (bun.getenvZ("CI")) |ci| { - if (strings.eqlComptime(ci, "false")) { + if (bun.env_var.CI.get()) |ci| { + if (!ci) { return; } } // Special case Heroku - if (bun.getenvZ("NODE")) |node| { + if (bun.env_var.NODE.get()) |node| { if (strings.containsComptime(node, "/app/.heroku/node/bin/node")) { name = "heroku"; return; diff --git a/src/cli.zig b/src/cli.zig index 76845a6e6d..981269f3cc 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -224,7 +224,7 @@ pub const HelpCommand = struct { if (comptime Environment.isDebug) { if (bun.argv.len == 1) { if (bun.Output.isAIAgent()) { - if (bun.getenvZ("npm_lifecycle_event")) |event| { + if (bun.env_var.npm_lifecycle_event.get()) |event| { if (bun.strings.hasPrefixComptime(event, "bd")) { // claude gets very confused by the help menu // let's give claude some self confidence. @@ -528,9 +528,9 @@ pub const Command = struct { // if we are bunx, but NOT a symlink to bun. when we run ` install`, we dont // want to recursively run bunx. so this check lets us peek back into bun install. if (args_iter.next()) |next| { - if (bun.strings.eqlComptime(next, "add") and bun.getRuntimeFeatureFlag(.BUN_INTERNAL_BUNX_INSTALL)) { + if (bun.strings.eqlComptime(next, "add") and bun.feature_flag.BUN_INTERNAL_BUNX_INSTALL.get()) { return .AddCommand; - } else if (bun.strings.eqlComptime(next, "exec") and bun.getRuntimeFeatureFlag(.BUN_INTERNAL_BUNX_INSTALL)) { + } else if (bun.strings.eqlComptime(next, "exec") and bun.feature_flag.BUN_INTERNAL_BUNX_INSTALL.get()) { return .ExecCommand; } } @@ -659,13 +659,13 @@ pub const Command = struct { /// function or that stack space is used up forever. pub fn start(allocator: std.mem.Allocator, log: *logger.Log) !void { if (comptime Environment.allow_assert) { - if (bun.getenvZ("MI_VERBOSE") == null) { + if (!bun.env_var.MI_VERBOSE.get()) { bun.mimalloc.mi_option_set_enabled(.verbose, false); } } // bun build --compile entry point - if (!bun.getRuntimeFeatureFlag(.BUN_BE_BUN)) { + if (!bun.feature_flag.BUN_BE_BUN.get()) { if (try bun.StandaloneModuleGraph.fromExecutable(bun.default_allocator)) |graph| { var offset_for_passthrough: usize = 0; @@ -1152,8 +1152,8 @@ pub const Command = struct { Command.Tag.CreateCommand => { const intro_text = \\Usage: - \\ bun create \ - \\ bun create \ [...flags] dest + \\ bun create \ + \\ bun create \ [...flags] dest \\ bun create \ [...flags] dest \\ \\Environment variables: diff --git a/src/cli/Arguments.zig b/src/cli/Arguments.zig index a87f471e99..2c55ffb5d1 100644 --- a/src/cli/Arguments.zig +++ b/src/cli/Arguments.zig @@ -242,11 +242,16 @@ pub fn loadConfigPath(allocator: std.mem.Allocator, auto_loaded: bool, config_pa } fn getHomeConfigPath(buf: *bun.PathBuffer) ?[:0]const u8 { - if (bun.getenvZ("XDG_CONFIG_HOME") orelse bun.getenvZ(bun.DotEnv.home_env)) |data_dir| { - var paths = [_]string{".bunfig.toml"}; + var paths = [_]string{".bunfig.toml"}; + + if (bun.env_var.XDG_CONFIG_HOME.get()) |data_dir| { return resolve_path.joinAbsStringBufZ(data_dir, buf, &paths, .auto); } + if (bun.env_var.HOME.get()) |home_dir| { + return resolve_path.joinAbsStringBufZ(home_dir, buf, &paths, .auto); + } + return null; } pub fn loadConfig(allocator: std.mem.Allocator, user_config_path_: ?string, ctx: Command.Context, comptime cmd: Command.Tag) OOM!void { @@ -595,7 +600,7 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C const preloads = args.options("--preload"); const preloads2 = args.options("--require"); const preloads3 = args.options("--import"); - const preload4 = bun.getenvZ("BUN_INSPECT_PRELOAD"); + const preload4 = bun.env_var.BUN_INSPECT_PRELOAD.get(); const total_preloads = ctx.preloads.len + preloads.len + preloads2.len + preloads3.len + (if (preload4 != null) @as(usize, 1) else @as(usize, 0)); if (total_preloads > 0) { @@ -803,10 +808,8 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C } else if (use_system_ca) { Bun__Node__CAStore = .system; } else { - if (bun.getenvZ("NODE_USE_SYSTEM_CA")) |val| { - if (val.len > 0 and val[0] == '1') { - Bun__Node__CAStore = .system; - } + if (bun.env_var.NODE_USE_SYSTEM_CA.get()) { + Bun__Node__CAStore = .system; } } diff --git a/src/cli/bunx_command.zig b/src/cli/bunx_command.zig index f679eb59b1..09f7c29108 100644 --- a/src/cli/bunx_command.zig +++ b/src/cli/bunx_command.zig @@ -772,7 +772,7 @@ pub const BunxCommand = struct { switch (spawn_result.status) { .exited => |exit| { if (exit.signal.valid()) { - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN.get()) { bun.crash_handler.suppressReporting(); } @@ -784,7 +784,7 @@ pub const BunxCommand = struct { } }, .signaled => |signal| { - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN.get()) { bun.crash_handler.suppressReporting(); } diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 1bea76377b..7edff915f2 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -1885,7 +1885,7 @@ pub const Example = struct { folders[1] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.stdDir(); } - if (env_loader.map.get(bun.DotEnv.home_env)) |home_dir| { + if (env_loader.map.get(bun.env_var.HOME.key())) |home_dir| { var parts = [_]string{ home_dir, BUN_CREATE_DIR }; const outdir_path = filesystem.absBuf(&parts, &home_dir_buf); folders[2] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.stdDir(); @@ -2301,7 +2301,7 @@ pub const CreateListExamplesCommand = struct { Output.prettyln("# You can also paste a GitHub repository:\n\n bun create ahfarmer/calculator calc\n\n", .{}); - if (env_loader.map.get(bun.DotEnv.home_env)) |homedir| { + if (env_loader.map.get(bun.env_var.HOME.key())) |homedir| { Output.prettyln( "This command is completely optional. To add a new local template, create a folder in {s}/.bun-create/. To publish a new template, git clone https://github.com/oven-sh/bun, add a new folder to the \"examples\" folder, and submit a PR.", .{homedir}, diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig index ac6aa4a594..e2ed22ead6 100644 --- a/src/cli/init_command.zig +++ b/src/cli/init_command.zig @@ -995,14 +995,14 @@ const Template = enum { } // Give some way to opt out. - if (bun.getenvTruthy("BUN_AGENT_RULE_DISABLED") or bun.getenvTruthy("CLAUDE_CODE_AGENT_RULE_DISABLED")) { + if (bun.env_var.BUN_AGENT_RULE_DISABLED.get() or bun.env_var.CLAUDE_CODE_AGENT_RULE_DISABLED.get()) { return false; } const pathbuffer = bun.path_buffer_pool.get(); defer bun.path_buffer_pool.put(pathbuffer); - return bun.which(pathbuffer, bun.getenvZ("PATH") orelse return false, bun.fs.FileSystem.instance.top_level_dir, "claude") != null; + return bun.which(pathbuffer, bun.env_var.PATH.get() orelse return false, bun.fs.FileSystem.instance.top_level_dir, "claude") != null; } pub fn createAgentRule() void { @@ -1054,15 +1054,13 @@ const Template = enum { fn isCursorInstalled() bool { // Give some way to opt-out. - if (bun.getenvTruthy("BUN_AGENT_RULE_DISABLED") or bun.getenvTruthy("CURSOR_AGENT_RULE_DISABLED")) { + if (bun.env_var.BUN_AGENT_RULE_DISABLED.get() or bun.env_var.CURSOR_AGENT_RULE_DISABLED.get()) { return false; } // Detect if they're currently using cursor. - if (bun.getenvZAnyCase("CURSOR_TRACE_ID")) |env| { - if (env.len > 0) { - return true; - } + if (bun.env_var.CURSOR_TRACE_ID.get()) { + return true; } if (Environment.isMac) { diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index a205cf6c95..b7cdb62024 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -7,7 +7,7 @@ pub const InstallCompletionsCommand = struct { var buf: bun.PathBuffer = undefined; // don't install it if it's already there - if (bun.which(&buf, bun.getenvZ("PATH") orelse cwd, cwd, bunx_name) != null) + if (bun.which(&buf, bun.env_var.PATH.get() orelse cwd, cwd, bunx_name) != null) return; // first try installing the symlink into the same directory as the bun executable @@ -16,7 +16,7 @@ pub const InstallCompletionsCommand = struct { var target = std.fmt.bufPrint(&target_buf, "{s}/" ++ bunx_name, .{std.fs.path.dirname(exe).?}) catch unreachable; std.posix.symlink(exe, target) catch { outer: { - if (bun.getenvZ("BUN_INSTALL")) |install_dir| { + if (bun.env_var.BUN_INSTALL.get()) |install_dir| { target = std.fmt.bufPrint(&target_buf, "{s}/bin/" ++ bunx_name, .{install_dir}) catch unreachable; std.posix.symlink(exe, target) catch break :outer; return; @@ -25,7 +25,7 @@ pub const InstallCompletionsCommand = struct { // if that fails, try $HOME/.bun/bin outer: { - if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + if (bun.env_var.HOME.get()) |home_dir| { target = std.fmt.bufPrint(&target_buf, "{s}/.bun/bin/" ++ bunx_name, .{home_dir}) catch unreachable; std.posix.symlink(exe, target) catch break :outer; return; @@ -34,7 +34,7 @@ pub const InstallCompletionsCommand = struct { // if that fails, try $HOME/.local/bin outer: { - if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + if (bun.env_var.HOME.get()) |home_dir| { target = std.fmt.bufPrint(&target_buf, "{s}/.local/bin/" ++ bunx_name, .{home_dir}) catch unreachable; std.posix.symlink(exe, target) catch break :outer; return; @@ -123,14 +123,14 @@ pub const InstallCompletionsCommand = struct { pub fn exec(allocator: std.mem.Allocator) !void { // Fail silently on auto-update. - const fail_exit_code: u8 = if (bun.getenvZ("IS_BUN_AUTO_UPDATE") == null) 1 else 0; + const fail_exit_code: u8 = if (!bun.env_var.IS_BUN_AUTO_UPDATE.get()) 1 else 0; var cwd_buf: bun.PathBuffer = undefined; var stdout = std.io.getStdOut(); var shell = ShellCompletions.Shell.unknown; - if (bun.getenvZ("SHELL")) |shell_name| { + if (bun.env_var.SHELL.platformGet()) |shell_name| { shell = ShellCompletions.Shell.fromEnv(@TypeOf(shell_name), shell_name); } @@ -169,7 +169,7 @@ pub const InstallCompletionsCommand = struct { else => {}, } - if (bun.getenvZ("IS_BUN_AUTO_UPDATE") == null) { + if (!bun.env_var.IS_BUN_AUTO_UPDATE.get()) { if (!stdout.isTty()) { try stdout.writeAll(shell.completions()); Global.exit(0); @@ -210,7 +210,7 @@ pub const InstallCompletionsCommand = struct { switch (shell) { .fish => { - if (bun.getenvZ("XDG_CONFIG_HOME")) |config_dir| { + if (bun.env_var.XDG_CONFIG_HOME.get()) |config_dir| { outer: { var paths = [_]string{ config_dir, "./fish/completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -219,7 +219,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("XDG_DATA_HOME")) |data_dir| { + if (bun.env_var.XDG_DATA_HOME.get()) |data_dir| { outer: { var paths = [_]string{ data_dir, "./fish/completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -229,7 +229,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + if (bun.env_var.HOME.get()) |home_dir| { outer: { var paths = [_]string{ home_dir, "./.config/fish/completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -260,7 +260,7 @@ pub const InstallCompletionsCommand = struct { } }, .zsh => { - if (bun.getenvZ("fpath")) |fpath| { + if (bun.env_var.fpath.get()) |fpath| { var splitter = std.mem.splitScalar(u8, fpath, ' '); while (splitter.next()) |dir| { @@ -269,7 +269,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("XDG_DATA_HOME")) |data_dir| { + if (bun.env_var.XDG_DATA_HOME.get()) |data_dir| { outer: { var paths = [_]string{ data_dir, "./zsh-completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -279,7 +279,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("BUN_INSTALL")) |home_dir| { + if (bun.env_var.BUN_INSTALL.get()) |home_dir| { outer: { completions_dir = home_dir; break :found std.fs.openDirAbsolute(home_dir, .{}) catch @@ -287,7 +287,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + if (bun.env_var.HOME.get()) |home_dir| { { outer: { var paths = [_]string{ home_dir, "./.oh-my-zsh/completions" }; @@ -320,7 +320,7 @@ pub const InstallCompletionsCommand = struct { } }, .bash => { - if (bun.getenvZ("XDG_DATA_HOME")) |data_dir| { + if (bun.env_var.XDG_DATA_HOME.get()) |data_dir| { outer: { var paths = [_]string{ data_dir, "./bash-completion/completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -329,7 +329,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("XDG_CONFIG_HOME")) |config_dir| { + if (bun.env_var.XDG_CONFIG_HOME.get()) |config_dir| { outer: { var paths = [_]string{ config_dir, "./bash-completion/completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -339,7 +339,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + if (bun.env_var.HOME.get()) |home_dir| { { outer: { var paths = [_]string{ home_dir, "./.oh-my-bash/custom/completions" }; @@ -439,7 +439,7 @@ pub const InstallCompletionsCommand = struct { // $ZDOTDIR/.zlogin // $ZDOTDIR/.zlogout - if (bun.getenvZ("ZDOTDIR")) |zdot_dir| { + if (bun.env_var.ZDOTDIR.get()) |zdot_dir| { bun.copy(u8, &zshrc_filepath, zdot_dir); bun.copy(u8, zshrc_filepath[zdot_dir.len..], "/.zshrc"); zshrc_filepath[zdot_dir.len + "/.zshrc".len] = 0; @@ -449,7 +449,7 @@ pub const InstallCompletionsCommand = struct { } second: { - if (bun.getenvZ(bun.DotEnv.home_env)) |zdot_dir| { + if (bun.env_var.HOME.get()) |zdot_dir| { bun.copy(u8, &zshrc_filepath, zdot_dir); bun.copy(u8, zshrc_filepath[zdot_dir.len..], "/.zshrc"); zshrc_filepath[zdot_dir.len + "/.zshrc".len] = 0; @@ -459,7 +459,7 @@ pub const InstallCompletionsCommand = struct { } third: { - if (bun.getenvZ(bun.DotEnv.home_env)) |zdot_dir| { + if (bun.env_var.HOME.get()) |zdot_dir| { bun.copy(u8, &zshrc_filepath, zdot_dir); bun.copy(u8, zshrc_filepath[zdot_dir.len..], "/.zshenv"); zshrc_filepath[zdot_dir.len + "/.zshenv".len] = 0; @@ -531,7 +531,6 @@ pub const InstallCompletionsCommand = struct { const string = []const u8; -const DotEnv = @import("../env_loader.zig"); const ShellCompletions = @import("./shell_completions.zig"); const fs = @import("../fs.zig"); const resolve_path = @import("../resolver/resolve_path.zig"); diff --git a/src/cli/package_manager_command.zig b/src/cli/package_manager_command.zig index a1d2acea64..cf296288c2 100644 --- a/src/cli/package_manager_command.zig +++ b/src/cli/package_manager_command.zig @@ -111,7 +111,7 @@ pub const PackageManagerCommand = struct { \\ bun pm version [increment] bump the version in package.json and create a git tag \\ increment patch, minor, major, prepatch, preminor, premajor, prerelease, from-git, or a specific version \\ bun pm pkg manage data in package.json - \\ get [key ...] + \\ get [key ...] \\ set key=value ... \\ delete key ... \\ fix auto-correct common package.json errors @@ -200,7 +200,7 @@ pub const PackageManagerCommand = struct { if (pm.options.global) { warner: { if (Output.enable_ansi_colors_stderr) { - if (bun.getenvZ("PATH")) |path| { + if (bun.env_var.PATH.get()) |path| { var path_iter = std.mem.tokenizeScalar(u8, path, std.fs.path.delimiter); while (path_iter.next()) |entry| { if (strings.eql(entry, output_path)) { diff --git a/src/cli/pm_version_command.zig b/src/cli/pm_version_command.zig index 21deb456cb..4f6c69768b 100644 --- a/src/cli/pm_version_command.zig +++ b/src/cli/pm_version_command.zig @@ -284,7 +284,7 @@ pub const PmVersionCommand = struct { \\ patch {s} → {s} \\ minor {s} → {s} \\ major {s} → {s} - \\ prerelease {s} → {s} + \\ prerelease {s} → {s} \\ ; Output.pretty(increment_help_text, .{ @@ -448,7 +448,7 @@ pub const PmVersionCommand = struct { fn isGitClean(cwd: []const u8) bun.OOM!bool { var path_buf: bun.PathBuffer = undefined; - const git_path = bun.which(&path_buf, bun.getenvZ("PATH") orelse "", cwd, "git") orelse { + const git_path = bun.which(&path_buf, bun.env_var.PATH.get() orelse "", cwd, "git") orelse { Output.errGeneric("git must be installed to use `bun pm version --git-tag-version`", .{}); Global.exit(1); }; @@ -481,7 +481,7 @@ pub const PmVersionCommand = struct { fn getVersionFromGit(allocator: std.mem.Allocator, cwd: []const u8) bun.OOM![]const u8 { var path_buf: bun.PathBuffer = undefined; - const git_path = bun.which(&path_buf, bun.getenvZ("PATH") orelse "", cwd, "git") orelse { + const git_path = bun.which(&path_buf, bun.env_var.PATH.get() orelse "", cwd, "git") orelse { Output.errGeneric("git must be installed to use `bun pm version from-git`", .{}); Global.exit(1); }; @@ -528,7 +528,7 @@ pub const PmVersionCommand = struct { fn gitCommitAndTag(allocator: std.mem.Allocator, version: []const u8, custom_message: ?[]const u8, cwd: []const u8) bun.OOM!void { var path_buf: bun.PathBuffer = undefined; - const git_path = bun.which(&path_buf, bun.getenvZ("PATH") orelse "", cwd, "git") orelse { + const git_path = bun.which(&path_buf, bun.env_var.PATH.get() orelse "", cwd, "git") orelse { Output.errGeneric("git must be installed to use `bun pm version --git-tag-version`", .{}); Global.exit(1); }; diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 7c5fbb0818..870c6a342e 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -274,7 +274,7 @@ pub const RunCommand = struct { }; const ipc_fd: ?bun.FD = if (!Environment.isWindows) blk: { - const node_ipc_fd = bun.getenvZ("NODE_CHANNEL_FD") orelse break :blk null; + const node_ipc_fd = bun.env_var.NODE_CHANNEL_FD.get() orelse break :blk null; const fd = std.fmt.parseInt(u31, node_ipc_fd, 10) catch break :blk null; break :blk bun.FD.fromNative(fd); } else null; // TODO: implement on Windows @@ -321,7 +321,7 @@ pub const RunCommand = struct { Output.prettyErrorln("error: script \"{s}\" was terminated by signal {}", .{ name, exit_code.signal.fmt(Output.enable_ansi_colors_stderr) }); Output.flush(); - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN.get()) { bun.crash_handler.suppressReporting(); } @@ -344,7 +344,7 @@ pub const RunCommand = struct { Output.flush(); } - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN.get()) { bun.crash_handler.suppressReporting(); } @@ -521,7 +521,7 @@ pub const RunCommand = struct { }); } - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN.get()) { bun.crash_handler.suppressReporting(); } @@ -538,7 +538,7 @@ pub const RunCommand = struct { }); } - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN.get()) { bun.crash_handler.suppressReporting(); } @@ -1502,10 +1502,7 @@ pub const RunCommand = struct { const preserve_symlinks = this_transpiler.resolver.opts.preserve_symlinks; defer this_transpiler.resolver.opts.preserve_symlinks = preserve_symlinks; this_transpiler.resolver.opts.preserve_symlinks = ctx.runtime_options.preserve_symlinks_main or - if (bun.getenvZ("NODE_PRESERVE_SYMLINKS_MAIN")) |env| - bun.strings.eqlComptime(env, "1") - else - false; + bun.env_var.NODE_PRESERVE_SYMLINKS_MAIN.get(); break :brk this_transpiler.resolver.resolve( this_transpiler.fs.top_level_dir, target_name, diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index ba35dd803e..8436e1394d 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -182,9 +182,9 @@ pub const JunitReporter = struct { const properties: PropertiesList = .{ .ci = brk: { - if (bun.getenvZ("GITHUB_RUN_ID")) |github_run_id| { - if (bun.getenvZ("GITHUB_SERVER_URL")) |github_server_url| { - if (bun.getenvZ("GITHUB_REPOSITORY")) |github_repository| { + if (bun.env_var.GITHUB_RUN_ID.get()) |github_run_id| { + if (bun.env_var.GITHUB_SERVER_URL.get()) |github_server_url| { + if (bun.env_var.GITHUB_REPOSITORY.get()) |github_repository| { if (github_run_id.len > 0 and github_server_url.len > 0 and github_repository.len > 0) { break :brk try std.fmt.allocPrint(allocator, "{s}/{s}/actions/runs/{s}", .{ github_server_url, github_repository, github_run_id }); } @@ -192,7 +192,7 @@ pub const JunitReporter = struct { } } - if (bun.getenvZ("CI_JOB_URL")) |ci_job_url| { + if (bun.env_var.CI_JOB_URL.get()) |ci_job_url| { if (ci_job_url.len > 0) { break :brk ci_job_url; } @@ -201,19 +201,19 @@ pub const JunitReporter = struct { break :brk ""; }, .commit = brk: { - if (bun.getenvZ("GITHUB_SHA")) |github_sha| { + if (bun.env_var.GITHUB_SHA.get()) |github_sha| { if (github_sha.len > 0) { break :brk github_sha; } } - if (bun.getenvZ("CI_COMMIT_SHA")) |sha| { + if (bun.env_var.CI_COMMIT_SHA.get()) |sha| { if (sha.len > 0) { break :brk sha; } } - if (bun.getenvZ("GIT_SHA")) |git_sha| { + if (bun.env_var.GIT_SHA.get()) |git_sha| { if (git_sha.len > 0) { break :brk git_sha; } diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index a372098a8b..8e8ebbcda9 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -557,7 +557,7 @@ pub const UpgradeCommand = struct { save_dir.deleteFileZ(tmpname) catch {}; Global.exit(1); } - } else if (Environment.isWindows) { + } else if (comptime Environment.isWindows) { // Run a powershell script to unzip the file const unzip_script = try std.fmt.allocPrint( ctx.allocator, @@ -570,9 +570,9 @@ pub const UpgradeCommand = struct { var buf: bun.PathBuffer = undefined; const powershell_path = - bun.which(&buf, bun.getenvZ("PATH") orelse "", "", "powershell") orelse + bun.which(&buf, bun.env_var.PATH.get() orelse "", "", "powershell") orelse hardcoded_system_powershell: { - const system_root = bun.getenvZ("SystemRoot") orelse "C:\\Windows"; + const system_root = bun.env_var.SYSTEMROOT.get() orelse "C:\\Windows"; const hardcoded_system_powershell = bun.path.joinAbsStringBuf(system_root, &buf, &.{ system_root, "System32\\WindowsPowerShell\\v1.0\\powershell.exe" }, .windows); if (bun.sys.exists(hardcoded_system_powershell)) { break :hardcoded_system_powershell hardcoded_system_powershell; diff --git a/src/compile_target.zig b/src/compile_target.zig index 5ec1c5cecc..b0f88742ec 100644 --- a/src/compile_target.zig +++ b/src/compile_target.zig @@ -66,7 +66,7 @@ pub fn isDefault(this: *const CompileTarget) bool { } pub fn toNPMRegistryURL(this: *const CompileTarget, buf: []u8) ![]const u8 { - if (bun.getenvZ("BUN_COMPILE_TARGET_TARBALL_URL")) |url| { + if (bun.env_var.BUN_COMPILE_TARGET_TARBALL_URL.get()) |url| { if (strings.hasPrefixComptime(url, "http://") or strings.hasPrefixComptime(url, "https://")) return url; } diff --git a/src/copy_file.zig b/src/copy_file.zig index 08093297cb..0920457995 100644 --- a/src/copy_file.zig +++ b/src/copy_file.zig @@ -147,7 +147,7 @@ pub fn canUseCopyFileRangeSyscall() bool { const result = can_use_copy_file_range.load(.monotonic); if (result == 0) { // This flag mostly exists to make other code more easily testable. - if (bun.getenvZ("BUN_CONFIG_DISABLE_COPY_FILE_RANGE") != null) { + if (bun.env_var.BUN_CONFIG_DISABLE_COPY_FILE_RANGE.get()) { debug("copy_file_range is disabled by BUN_CONFIG_DISABLE_COPY_FILE_RANGE", .{}); can_use_copy_file_range.store(-1, .monotonic); return false; @@ -179,7 +179,7 @@ pub fn can_use_ioctl_ficlone() bool { const result = can_use_ioctl_ficlone_.load(.monotonic); if (result == 0) { // This flag mostly exists to make other code more easily testable. - if (bun.getenvZ("BUN_CONFIG_DISABLE_ioctl_ficlonerange") != null) { + if (bun.env_var.BUN_CONFIG_DISABLE_ioctl_ficlonerange.get()) { debug("ioctl_ficlonerange is disabled by BUN_CONFIG_DISABLE_ioctl_ficlonerange", .{}); can_use_ioctl_ficlone_.store(-1, .monotonic); return false; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index b1fb33fa82..d1d98ff074 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -241,7 +241,7 @@ pub fn crashHandler( } else if (bun.analytics.Features.unsupported_uv_function > 0) { const name = unsupported_uv_function orelse ""; const fmt = - \\Bun encountered a crash when running a NAPI module that tried to call + \\Bun encountered a crash when running a NAPI module that tried to call \\the {s} libuv function. \\ \\Bun is actively working on supporting all libuv functions for POSIX @@ -403,7 +403,7 @@ pub fn crashHandler( } else if (bun.analytics.Features.unsupported_uv_function > 0) { const name = unsupported_uv_function orelse ""; const fmt = - \\Bun encountered a crash when running a NAPI module that tried to call + \\Bun encountered a crash when running a NAPI module that tried to call \\the {s} libuv function. \\ \\Bun is actively working on supporting all libuv functions for POSIX @@ -583,7 +583,7 @@ pub fn handleRootError(err: anyerror, error_return_trace: ?*std.builtin.StackTra }, ); - if (bun.getenvZ("USER")) |user| { + if (bun.env_var.USER.get()) |user| { if (user.len > 0) { Output.prettyError( \\ @@ -652,7 +652,7 @@ pub fn handleRootError(err: anyerror, error_return_trace: ?*std.builtin.StackTra }, ); - if (bun.getenvZ("USER")) |user| { + if (bun.env_var.USER.get()) |user| { if (user.len > 0) { Output.prettyError( \\ @@ -699,7 +699,7 @@ pub fn handleRootError(err: anyerror, error_return_trace: ?*std.builtin.StackTra ); if (bun.Environment.isLinux) { - if (bun.getenvZ("USER")) |user| { + if (bun.env_var.USER.get()) |user| { if (user.len > 0) { Output.prettyError( \\ @@ -804,7 +804,7 @@ pub fn reportBaseUrl() []const u8 { }; return static.base_url orelse { const computed = computed: { - if (bun.getenvZ("BUN_CRASH_REPORT_URL")) |url| { + if (bun.env_var.BUN_CRASH_REPORT_URL.get()) |url| { break :computed bun.strings.withoutTrailingSlash(url); } break :computed default_report_base_url; @@ -1412,18 +1412,13 @@ fn isReportingEnabled() bool { if (suppress_reporting) return false; // If trying to test the crash handler backend, implicitly enable reporting - if (bun.getenvZ("BUN_CRASH_REPORT_URL")) |value| { + if (bun.env_var.BUN_CRASH_REPORT_URL.get()) |value| { return value.len > 0; } // Environment variable to specifically enable or disable reporting - if (bun.getenvZ("BUN_ENABLE_CRASH_REPORTING")) |value| { - if (value.len > 0) { - if (bun.strings.eqlComptime(value, "1")) { - return true; - } - return false; - } + if (bun.env_var.BUN_ENABLE_CRASH_REPORTING.get()) |enable_crash_reporting| { + return enable_crash_reporting; } // Debug builds shouldn't report to the default url by default @@ -1512,7 +1507,7 @@ fn report(url: []const u8) void { var buf2: bun.PathBuffer = undefined; const curl = bun.which( &buf, - bun.getenvZ("PATH") orelse return, + bun.env_var.PATH.get() orelse return, bun.getcwd(&buf2) catch return, "curl", ) orelse return; @@ -2265,7 +2260,7 @@ export fn CrashHandler__setInsideNativePlugin(name: ?[*:0]const u8) callconv(.C) export fn CrashHandler__unsupportedUVFunction(name: ?[*:0]const u8) callconv(.C) void { bun.analytics.Features.unsupported_uv_function += 1; unsupported_uv_function = name; - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_ON_UV_STUB)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_ON_UV_STUB.get()) { suppressReporting(); } std.debug.panic("unsupported uv function: {s}", .{name.?}); diff --git a/src/env_loader.zig b/src/env_loader.zig index fb1787f758..ef876173ec 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -1332,8 +1332,6 @@ pub const Map = struct { pub var instance: ?*Loader = null; -pub const home_env = if (Environment.isWindows) "USERPROFILE" else "HOME"; - const string = []const u8; const Fs = @import("./fs.zig"); diff --git a/src/env_var.zig b/src/env_var.zig new file mode 100644 index 0000000000..99b35005f3 --- /dev/null +++ b/src/env_var.zig @@ -0,0 +1,656 @@ +//! Unified module for controlling and managing environment variables in Bun. +//! +//! This library uses metaprogramming to achieve type-safe accessors for environment variables. +//! Calling .get() on any of the environment variables will return the correct environment variable +//! type, whether it's a string, unsigned or boolean. This library also caches the environment +//! variables for you, for slightly faster access. +//! +//! If default values are provided, the .get() method is guaranteed not to return a nullable type, +//! whereas if no default is provided, the .get() method will return an optional type. +//! +//! TODO(markovejnovic): It would be neat if this library supported loading floats as +//! well as strings, integers and booleans, but for now this will do. +//! +//! TODO(markovejnovic): As this library migrates away from bun.getenvZ, it should return +//! NUL-terminated slices, rather than plain slices. Perhaps there should be a +//! .getZ() accessor? +//! +//! TODO(markovejnovic): This current implementation kind of does redundant work. Instead of +//! scanning envp, and preparing everything on bootup, we lazily load +//! everything. This means that we potentially scan through envp a lot of +//! times, even though we could only do it once. + +pub const AGENT = New(kind.string, "AGENT", .{}); +pub const BUN_AGENT_RULE_DISABLED = New(kind.boolean, "BUN_AGENT_RULE_DISABLED", .{ .default = false }); +pub const BUN_COMPILE_TARGET_TARBALL_URL = New(kind.string, "BUN_COMPILE_TARGET_TARBALL_URL", .{}); +pub const BUN_CONFIG_DISABLE_COPY_FILE_RANGE = New(kind.boolean, "BUN_CONFIG_DISABLE_COPY_FILE_RANGE", .{ .default = false }); +pub const BUN_CONFIG_DISABLE_ioctl_ficlonerange = New(kind.boolean, "BUN_CONFIG_DISABLE_ioctl_ficlonerange", .{ .default = false }); +/// TODO(markovejnovic): Legacy usage had the default at 30, even though a the attached comment +/// quoted: Amazon Web Services recommends 5 seconds: +/// https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/jvm-ttl-dns.html +/// +/// It's unclear why this was done. +pub const BUN_CONFIG_DNS_TIME_TO_LIVE_SECONDS = New(kind.unsigned, "BUN_CONFIG_DNS_TIME_TO_LIVE_SECONDS", .{ .default = 30 }); +pub const BUN_CRASH_REPORT_URL = New(kind.string, "BUN_CRASH_REPORT_URL", .{}); +pub const BUN_DEBUG = New(kind.string, "BUN_DEBUG", .{}); +pub const BUN_DEBUG_ALL = New(kind.boolean, "BUN_DEBUG_ALL", .{}); +pub const BUN_DEBUG_CSS_ORDER = New(kind.boolean, "BUN_DEBUG_CSS_ORDER", .{ .default = false }); +pub const BUN_DEBUG_ENABLE_RESTORE_FROM_TRANSPILER_CACHE = New(kind.boolean, "BUN_DEBUG_ENABLE_RESTORE_FROM_TRANSPILER_CACHE", .{ .default = false }); +pub const BUN_DEBUG_HASH_RANDOM_SEED = New(kind.unsigned, "BUN_DEBUG_HASH_RANDOM_SEED", .{ .deser = .{ .error_handling = .not_set } }); +pub const BUN_DEBUG_QUIET_LOGS = New(kind.boolean, "BUN_DEBUG_QUIET_LOGS", .{}); +pub const BUN_DEBUG_TEST_TEXT_LOCKFILE = New(kind.boolean, "BUN_DEBUG_TEST_TEXT_LOCKFILE", .{ .default = false }); +pub const BUN_DEV_SERVER_TEST_RUNNER = New(kind.string, "BUN_DEV_SERVER_TEST_RUNNER", .{}); +pub const BUN_ENABLE_CRASH_REPORTING = New(kind.boolean, "BUN_ENABLE_CRASH_REPORTING", .{}); +pub const BUN_FEATURE_FLAG_DUMP_CODE = New(kind.string, "BUN_FEATURE_FLAG_DUMP_CODE", .{}); +/// TODO(markovejnovic): It's unclear why the default here is 100_000, but this was legacy behavior +/// so we'll keep it for now. +pub const BUN_INOTIFY_COALESCE_INTERVAL = New(kind.unsigned, "BUN_INOTIFY_COALESCE_INTERVAL", .{ .default = 100_000 }); +pub const BUN_INSPECT = New(kind.string, "BUN_INSPECT", .{ .default = "" }); +pub const BUN_INSPECT_CONNECT_TO = New(kind.string, "BUN_INSPECT_CONNECT_TO", .{ .default = "" }); +pub const BUN_INSPECT_PRELOAD = New(kind.string, "BUN_INSPECT_PRELOAD", .{}); +pub const BUN_INSTALL = New(kind.string, "BUN_INSTALL", .{}); +pub const BUN_INSTALL_BIN = New(kind.string, "BUN_INSTALL_BIN", .{}); +pub const BUN_INSTALL_GLOBAL_DIR = New(kind.string, "BUN_INSTALL_GLOBAL_DIR", .{}); +pub const BUN_NEEDS_PROC_SELF_WORKAROUND = New(kind.boolean, "BUN_NEEDS_PROC_SELF_WORKAROUND", .{ .default = false }); +pub const BUN_OPTIONS = New(kind.string, "BUN_OPTIONS", .{}); +pub const BUN_POSTGRES_SOCKET_MONITOR = New(kind.string, "BUN_POSTGRES_SOCKET_MONITOR", .{}); +pub const BUN_POSTGRES_SOCKET_MONITOR_READER = New(kind.string, "BUN_POSTGRES_SOCKET_MONITOR_READER", .{}); +pub const BUN_RUNTIME_TRANSPILER_CACHE_PATH = New(kind.string, "BUN_RUNTIME_TRANSPILER_CACHE_PATH", .{}); +pub const BUN_SSG_DISABLE_STATIC_ROUTE_VISITOR = New(kind.boolean, "BUN_SSG_DISABLE_STATIC_ROUTE_VISITOR", .{ .default = false }); +pub const BUN_TCC_OPTIONS = New(kind.string, "BUN_TCC_OPTIONS", .{}); +pub const BUN_TMPDIR = New(kind.string, "BUN_TMPDIR", .{}); +pub const BUN_TRACK_LAST_FN_NAME = New(kind.boolean, "BUN_TRACK_LAST_FN_NAME", .{ .default = false }); +pub const BUN_TRACY_PATH = New(kind.string, "BUN_TRACY_PATH", .{}); +pub const BUN_WATCHER_TRACE = New(kind.string, "BUN_WATCHER_TRACE", .{}); +pub const CI = New(kind.boolean, "CI", .{}); +pub const CI_COMMIT_SHA = New(kind.string, "CI_COMMIT_SHA", .{}); +pub const CI_JOB_URL = New(kind.string, "CI_JOB_URL", .{}); +pub const CLAUDE_CODE_AGENT_RULE_DISABLED = New(kind.boolean, "CLAUDE_CODE_AGENT_RULE_DISABLED", .{ .default = false }); +pub const CLAUDECODE = New(kind.boolean, "CLAUDECODE", .{ .default = false }); +pub const COLORTERM = New(kind.string, "COLORTERM", .{}); +pub const CURSOR_AGENT_RULE_DISABLED = New(kind.boolean, "CURSOR_AGENT_RULE_DISABLED", .{ .default = false }); +pub const CURSOR_TRACE_ID = New(kind.boolean, "CURSOR_TRACE_ID", .{ .default = false }); +pub const DO_NOT_TRACK = New(kind.boolean, "DO_NOT_TRACK", .{ .default = false }); +pub const DYLD_ROOT_PATH = PlatformSpecificNew(kind.string, "DYLD_ROOT_PATH", null, .{}); +/// TODO(markovejnovic): We should support enums in this library, and force_color's usage is, +/// indeed, an enum. The 80-20 is to make it an unsigned value (which also works well). +pub const FORCE_COLOR = New(kind.unsigned, "FORCE_COLOR", .{ .deser = .{ .error_handling = .truthy_cast, .empty_string_as = .{ .value = 1 } } }); +pub const fpath = PlatformSpecificNew(kind.string, "fpath", null, .{}); +pub const GIT_SHA = New(kind.string, "GIT_SHA", .{}); +pub const GITHUB_ACTIONS = New(kind.boolean, "GITHUB_ACTIONS", .{ .default = false }); +pub const GITHUB_REPOSITORY = New(kind.string, "GITHUB_REPOSITORY", .{}); +pub const GITHUB_RUN_ID = New(kind.string, "GITHUB_RUN_ID", .{}); +pub const GITHUB_SERVER_URL = New(kind.string, "GITHUB_SERVER_URL", .{}); +pub const GITHUB_SHA = New(kind.string, "GITHUB_SHA", .{}); +pub const GITHUB_WORKSPACE = New(kind.string, "GITHUB_WORKSPACE", .{}); +pub const HOME = PlatformSpecificNew(kind.string, "HOME", "USERPROFILE", .{}); +pub const HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET = New(kind.string, "HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET", .{}); +pub const IS_BUN_AUTO_UPDATE = New(kind.boolean, "IS_BUN_AUTO_UPDATE", .{ .default = false }); +pub const JENKINS_URL = New(kind.string, "JENKINS_URL", .{}); +/// Dump mimalloc statistics at the end of the process. Note that this is not the same as +/// `MIMALLOC_VERBOSE`, documented here: https://microsoft.github.io/mimalloc/environment.html +pub const MI_VERBOSE = New(kind.boolean, "MI_VERBOSE", .{ .default = false }); +pub const NO_COLOR = New(kind.boolean, "NO_COLOR", .{ .default = false }); +pub const NODE = New(kind.string, "NODE", .{}); +pub const NODE_CHANNEL_FD = New(kind.string, "NODE_CHANNEL_FD", .{}); +pub const NODE_PRESERVE_SYMLINKS_MAIN = New(kind.boolean, "NODE_PRESERVE_SYMLINKS_MAIN", .{ .default = false }); +pub const NODE_USE_SYSTEM_CA = New(kind.boolean, "NODE_USE_SYSTEM_CA", .{ .default = false }); +pub const npm_lifecycle_event = New(kind.string, "npm_lifecycle_event", .{}); +pub const PATH = New(kind.string, "PATH", .{}); +pub const REPL_ID = New(kind.boolean, "REPL_ID", .{ .default = false }); +pub const RUNNER_DEBUG = New(kind.boolean, "RUNNER_DEBUG", .{ .default = false }); +pub const SDKROOT = PlatformSpecificNew(kind.string, "SDKROOT", null, .{}); +pub const SHELL = PlatformSpecificNew(kind.string, "SHELL", null, .{}); +/// C:\Windows, for example. +/// Note: Do not use this variable directly -- use os.zig's implementation instead. +pub const SYSTEMROOT = PlatformSpecificNew(kind.string, null, "SYSTEMROOT", .{}); +pub const TEMP = PlatformSpecificNew(kind.string, null, "TEMP", .{}); +pub const TERM = New(kind.string, "TERM", .{}); +pub const TERM_PROGRAM = New(kind.string, "TERM_PROGRAM", .{}); +pub const TMP = PlatformSpecificNew(kind.string, null, "TMP", .{}); +pub const TMPDIR = PlatformSpecificNew(kind.string, "TMPDIR", null, .{}); +pub const TMUX = New(kind.string, "TMUX", .{}); +pub const TODIUM = New(kind.string, "TODIUM", .{}); +pub const USER = PlatformSpecificNew(kind.string, "USER", "USERNAME", .{}); +pub const WANTS_LOUD = New(kind.boolean, "WANTS_LOUD", .{ .default = false }); +/// The same as system_root. +/// Note: Do not use this variable directly -- use os.zig's implementation instead. +/// TODO(markovejnovic): Perhaps we could add support for aliases in the library, so you could +/// specify both WINDIR and SYSTEMROOT and the loader would check both? +pub const WINDIR = PlatformSpecificNew(kind.string, null, "WINDIR", .{}); +/// XDG Base Directory Specification variables. +/// For some reason, legacy usage respected these even on Windows. To avoid compatibility issues, +/// we respect them too. +pub const XDG_CACHE_HOME = New(kind.string, "XDG_CACHE_HOME", .{}); +pub const XDG_CONFIG_HOME = New(kind.string, "XDG_CONFIG_HOME", .{}); +pub const XDG_DATA_HOME = New(kind.string, "XDG_DATA_HOME", .{}); +pub const ZDOTDIR = New(kind.string, "ZDOTDIR", .{}); + +pub const feature_flag = struct { + pub const BUN_ASSUME_PERFECT_INCREMENTAL = newFeatureFlag("BUN_ASSUME_PERFECT_INCREMENTAL", .{ .default = null }); + pub const BUN_BE_BUN = newFeatureFlag("BUN_BE_BUN", .{}); + pub const BUN_DEBUG_NO_DUMP = newFeatureFlag("BUN_DEBUG_NO_DUMP", .{}); + pub const BUN_DESTRUCT_VM_ON_EXIT = newFeatureFlag("BUN_DESTRUCT_VM_ON_EXIT", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_DNS_CACHE = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_DNS_CACHE", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_INSTALL_INDEX = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_INSTALL_INDEX", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_IO_POOL = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_IO_POOL", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_IPV4 = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_IPV4", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_IPV6 = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_IPV6", .{}); + /// The RedisClient supports auto-pipelining by default. This flag disables that behavior. + pub const BUN_FEATURE_FLAG_DISABLE_REDIS_AUTO_PIPELINING = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_REDIS_AUTO_PIPELINING", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_RWF_NONBLOCK = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_RWF_NONBLOCK", .{}); + pub const BUN_DISABLE_SLOW_LIFECYCLE_SCRIPT_LOGGING = newFeatureFlag("BUN_DISABLE_SLOW_LIFECYCLE_SCRIPT_LOGGING", .{}); + pub const BUN_DISABLE_SOURCE_CODE_PREVIEW = newFeatureFlag("BUN_DISABLE_SOURCE_CODE_PREVIEW", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING", .{}); + pub const BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW = newFeatureFlag("BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE", .{}); + pub const BUN_DUMP_STATE_ON_CRASH = newFeatureFlag("BUN_DUMP_STATE_ON_CRASH", .{}); + pub const BUN_ENABLE_EXPERIMENTAL_SHELL_BUILTINS = newFeatureFlag("BUN_ENABLE_EXPERIMENTAL_SHELL_BUILTINS", .{}); + pub const BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE = newFeatureFlag("BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE", .{}); + pub const BUN_FEATURE_FLAG_FORCE_IO_POOL = newFeatureFlag("BUN_FEATURE_FLAG_FORCE_IO_POOL", .{}); + pub const BUN_FEATURE_FLAG_FORCE_WINDOWS_JUNCTIONS = newFeatureFlag("BUN_FEATURE_FLAG_FORCE_WINDOWS_JUNCTIONS", .{}); + pub const BUN_INSTRUMENTS = newFeatureFlag("BUN_INSTRUMENTS", .{}); + pub const BUN_INTERNAL_BUNX_INSTALL = newFeatureFlag("BUN_INTERNAL_BUNX_INSTALL", .{}); + pub const BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN = newFeatureFlag("BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN", .{}); + pub const BUN_INTERNAL_SUPPRESS_CRASH_ON_NAPI_ABORT = newFeatureFlag("BUN_INTERNAL_SUPPRESS_CRASH_ON_NAPI_ABORT", .{}); + pub const BUN_INTERNAL_SUPPRESS_CRASH_ON_PROCESS_KILL_SELF = newFeatureFlag("BUN_INTERNAL_SUPPRESS_CRASH_ON_PROCESS_KILL_SELF", .{}); + pub const BUN_INTERNAL_SUPPRESS_CRASH_ON_UV_STUB = newFeatureFlag("BUN_INTERNAL_SUPPRESS_CRASH_ON_UV_STUB", .{}); + pub const BUN_FEATURE_FLAG_LAST_MODIFIED_PRETEND_304 = newFeatureFlag("BUN_FEATURE_FLAG_LAST_MODIFIED_PRETEND_304", .{}); + pub const BUN_NO_CODESIGN_MACHO_BINARY = newFeatureFlag("BUN_NO_CODESIGN_MACHO_BINARY", .{}); + pub const BUN_FEATURE_FLAG_NO_LIBDEFLATE = newFeatureFlag("BUN_FEATURE_FLAG_NO_LIBDEFLATE", .{}); + pub const NODE_NO_WARNINGS = newFeatureFlag("NODE_NO_WARNINGS", .{}); + pub const BUN_TRACE = newFeatureFlag("BUN_TRACE", .{}); +}; + +/// Interface between each of the different EnvVar types and the common logic. +fn CacheOutput(comptime ValueType: type) type { + return union(enum) { + /// The environment variable hasn't been loaded yet. + unknown: void, + /// The environment variable has been loaded but its not set. + not_set: void, + /// The environment variable is set to a value. + value: ValueType, + }; +} + +fn CacheConfigurationType(comptime CtorOptionsType: type) type { + return struct { + var_name: []const u8, + opts: CtorOptionsType, + }; +} + +/// Structure which encodes the different types of environment variables supported. +/// +/// This requires the following static members: +/// +/// - `ValueType`: The underlying environment variable type if one is set. For +/// example, a string `$PATH` ought return a `[]const u8` when set. +/// - `Cache`: A struct implementing the following methods: +/// - `getCached() CacheOutput(ValueType)`: Retrieve the cached value of the +/// environment variable, if any. +/// - `deserAndInvalidate(raw_env: ?[]const u8) ?ValueType` +/// - `CtorOptions`: A struct containing the options passed to the constructor of the environment +/// variable definition. +/// +/// This type will communicate with the common logic via the `CacheOutput` type. +const kind = struct { + const string = struct { + const ValueType = []const u8; + const Input = CacheConfigurationType(CtorOptions); + const Output = CacheOutput(ValueType); + const CtorOptions = struct { + default: ?ValueType = null, + }; + + fn Cache(comptime ip: Input) type { + _ = ip; + + const PointerType = ?[*]const u8; + const LenType = usize; + + return struct { + const Self = @This(); + + const not_loaded_sentinel = struct { + const ptr: PointerType = null; + const len: LenType = std.math.maxInt(LenType); + }; + + const not_set_sentinel = struct { + const ptr: PointerType = null; + const len: LenType = std.math.maxInt(LenType) - 1; + }; + + ptr_value: std.atomic.Value(PointerType) = .init(null), + len_value: std.atomic.Value(LenType) = .init(std.math.maxInt(LenType)), + + fn getCached(self: *Self) Output { + const len = self.len_value.load(.acquire); + + if (len == not_loaded_sentinel.len) { + return .{ .unknown = {} }; + } + + if (len == not_set_sentinel.len) { + return .{ .not_set = {} }; + } + + const ptr = self.ptr_value.load(.monotonic); + + return .{ .value = ptr.?[0..len] }; + } + + inline fn deserAndInvalidate(self: *Self, raw_env: ?[]const u8) ?ValueType { + // The implementation is racy and allows two threads to both set the value at + // the same time, as long as the value they are setting is the same. This is + // difficult to write an assertion for since it requires the DEV path take a + // .swap() path rather than a plain .store(). + + if (raw_env) |ev| { + self.ptr_value.store(ev.ptr, .monotonic); + self.len_value.store(ev.len, .release); + } else { + self.ptr_value.store(not_set_sentinel.ptr, .monotonic); + self.len_value.store(not_set_sentinel.len, .release); + } + + return raw_env; + } + }; + } + }; + + const boolean = struct { + const ValueType = bool; + const Input = CacheConfigurationType(CtorOptions); + const Output = CacheOutput(ValueType); + const CtorOptions = struct { + default: ?ValueType = null, + }; + + fn stringIsTruthy(s: []const u8) bool { + // Most values are considered truthy, except for "", "0", "false", "no", and "off". + const false_values = .{ "", "0", "false", "no", "off" }; + + inline for (false_values) |tv| { + if (std.ascii.eqlIgnoreCase(s, tv)) { + return false; + } + } + + return true; + } + + // This is a template which ignores its parameter, but is necessary so that a separate + // Cache type is emitted for every environment variable. + fn Cache(comptime ip: Input) type { + return struct { + const Self = @This(); + + const StoredType = enum(u8) { unknown, not_set, no, yes }; + + value: std.atomic.Value(StoredType) = .init(.unknown), + + inline fn getCached(self: *Self) Output { + _ = ip; + + const cached = self.value.load(.monotonic); + switch (cached) { + .unknown => { + @branchHint(.unlikely); + return .{ .unknown = {} }; + }, + .not_set => { + return .{ .not_set = {} }; + }, + .no => { + return .{ .value = false }; + }, + .yes => { + return .{ .value = true }; + }, + } + } + + inline fn deserAndInvalidate(self: *Self, raw_env: ?[]const u8) ?ValueType { + if (raw_env == null) { + self.value.store(.not_set, .monotonic); + return null; + } + + const string_is_truthy = stringIsTruthy(raw_env.?); + self.value.store(if (string_is_truthy) .yes else .no, .monotonic); + return string_is_truthy; + } + }; + } + }; + + const unsigned = struct { + const ValueType = u64; + const Input = CacheConfigurationType(CtorOptions); + const Output = CacheOutput(ValueType); + const CtorOptions = struct { + default: ?ValueType = null, + deser: struct { + /// Control how deserializing and deserialization errors are handled. + error_handling: enum { + /// panic on deserialization errors. + panic, + /// Ignore deserialization errors and treat the variable as not set. + not_set, + /// Fallback to default. + default_fallback, + /// Formatting errors are treated as truthy values. + /// + /// If this library fails to parse the value as an integer and truthy cast is + /// enabled, truthy values will be set to 1 or 0. + /// + /// Note: Most values are considered truthy, except for "", "0", "false", "no", + /// and "off". + truthy_cast, + } = .panic, + + /// Control what empty strings are treated as. + empty_string_as: union(enum) { + /// Empty strings are handled as the given value. + value: ValueType, + /// Empty strings are treated as deserialization errors. + erroneous: void, + } = .erroneous, + } = .{}, + }; + + fn Cache(comptime ip: Input) type { + return struct { + const Self = @This(); + + const StoredType = ValueType; + + /// The value meaning an environment variable that hasn't been loaded yet. + const unknown_sentinel: comptime_int = std.math.maxInt(StoredType); + /// The unique value representing an environment variable that is not set. + const not_set_sentinel: comptime_int = std.math.maxInt(StoredType) - 1; + + value: std.atomic.Value(StoredType) = .init(unknown_sentinel), + + inline fn getCached(self: *Self) Output { + switch (self.value.load(.monotonic)) { + unknown_sentinel => { + @branchHint(.unlikely); + return .{ .unknown = {} }; + }, + not_set_sentinel => { + return .{ .not_set = {} }; + }, + else => |v| { + return .{ .value = v }; + }, + } + } + + inline fn deserAndInvalidate(self: *Self, raw_env: ?[]const u8) ?ValueType { + if (raw_env == null) { + self.value.store(not_set_sentinel, .monotonic); + return null; + } + + if (std.mem.eql(u8, raw_env.?, "")) { + switch (ip.opts.deser.empty_string_as) { + .value => |v| { + self.value.store(v, .monotonic); + return v; + }, + .erroneous => { + return self.handleError(raw_env.?, "is an empty string"); + }, + } + } + + const formatted = std.fmt.parseInt(StoredType, raw_env.?, 10) catch |err| { + switch (err) { + error.Overflow => { + return self.handleError(raw_env.?, "overflows u64"); + }, + error.InvalidCharacter => { + return self.handleError(raw_env.?, "is not a valid integer"); + }, + } + }; + + if (formatted == not_set_sentinel or formatted == unknown_sentinel) { + return self.handleError(raw_env.?, "is a reserved value"); + } + + self.value.store(formatted, .monotonic); + return formatted; + } + + fn handleError( + self: *Self, + raw_env: []const u8, + comptime reason: []const u8, + ) ?ValueType { + const base_fmt = "Environment variable '{s}' has value '{s}' which "; + const fmt = base_fmt ++ reason ++ "."; + const missing_default_fmt = "Environment variable '{s}' is configured to " ++ + "fallback to default on {s}, but no default is set."; + + switch (ip.opts.deser.error_handling) { + .panic => { + bun.Output.panic(fmt, .{ ip.var_name, raw_env }); + }, + .not_set => { + self.value.store(not_set_sentinel, .monotonic); + return null; + }, + .truthy_cast => { + if (kind.boolean.stringIsTruthy(raw_env)) { + self.value.store(1, .monotonic); + return 1; + } else { + self.value.store(0, .monotonic); + return 0; + } + }, + .default_fallback => { + if (comptime ip.opts.default) |d| { + return deserAndInvalidate(d); + } + @compileError(std.fmt.comptimePrint(missing_default_fmt, .{ + ip.var_name, + "default_fallback", + })); + }, + } + } + }; + } + }; +}; + +/// Create a new environment variable definition. +/// +/// The resulting type has methods for interacting with the environment variable. +/// +/// Technically, none of the operations here are thread-safe, so writing to environment variables +/// does not guarantee that other threads will see the changes. You should avoid writing to +/// environment variables. +fn New( + comptime VariantType: type, + comptime key: [:0]const u8, + comptime opts: VariantType.CtorOptions, +) type { + return PlatformSpecificNew(VariantType, key, key, opts); +} + +/// Identical to new, except it allows you to specify different keys for POSIX and Windows. +/// +/// If the current platform does not have a key specified, all methods that attempt to read the +/// environment variable will fail at compile time, except for `platformGet` and `platformKey`, +/// which will return null instead. +fn PlatformSpecificNew( + comptime VariantType: type, + comptime posix_key: ?[:0]const u8, + comptime windows_key: ?[:0]const u8, + comptime opts: VariantType.CtorOptions, +) type { + const DefaultType = if (comptime opts.default) |d| @TypeOf(d) else void; + + const comptime_key: []const u8 = + if (posix_key) |pk| pk else if (windows_key) |wk| wk else ""; + + if (posix_key == null and windows_key == null) { + @compileError("Environment variable " ++ comptime_key ++ " has no keys for POSIX " ++ + "nor Windows specified. Provide a key for either POSIX or Windows."); + } + + const KeyType = [:0]const u8; + + // Return type as returned by each of the variants of kind. + const ValueType = VariantType.ValueType; + + // The actual return type of public methods. + const ReturnType = if (opts.default != null) ValueType else ?ValueType; + + return struct { + const Self = @This(); + + var cache: VariantType.Cache(.{ .var_name = comptime_key, .opts = opts }) = .{}; + + /// Attempt to retrieve the value of the environment variable for the current platform, if + /// the current platform has a supported definition. Returns null otherwise, unlike the + /// other methods which will fail at compile time if the platform is unsupported. + pub fn platformGet() ?ValueType { + // Get the platform-specific key + const platform_key: ?KeyType = if (comptime bun.Environment.isPosix) + posix_key + else if (comptime bun.Environment.isWindows) + windows_key + else + null; + + // If platform doesn't have a key, return null + const k = platform_key orelse return null; + + // Inline the logic from get() without calling assertPlatformSupported() + switch (cache.getCached()) { + .unknown => { + @branchHint(.unlikely); + + const env_var = bun.getenvZ(k); + const maybe_reloaded = cache.deserAndInvalidate(env_var); + + if (maybe_reloaded) |v| return v; + if (opts.default) |d| { + return d; + } + + return null; + }, + .not_set => { + if (opts.default) |d| { + return d; + } + return null; + }, + .value => |v| return v, + } + } + + /// Equal to `.platformKey()` except fails to compile if current platform is supported. + pub fn key() KeyType { + assertPlatformSupported(); + return Self.platformKey().?; + } + + /// Retrieve the key of the environment variable for the current platform, if any. + pub fn platformKey() ?KeyType { + if (bun.Environment.isPosix) { + return posix_key; + } + + if (bun.Environment.isWindows) { + return windows_key; + } + + return null; + } + + /// Retrieve the value of the environment variable, loading it if necessary. + /// Fails if the current platform is unsupported. + pub fn get() ReturnType { + assertPlatformSupported(); + + const cached_result = cache.getCached(); + + switch (cached_result) { + .unknown => { + @branchHint(.unlikely); + return getForceReload(); + }, + .not_set => { + if (opts.default) |d| { + return d; + } + return null; + }, + .value => |v| { + return v; + }, + } + } + + /// Retrieve the value of the environment variable, reloading it from the environment. + /// Fails if the current platform is unsupported. + fn getForceReload() ReturnType { + assertPlatformSupported(); + const env_var = bun.getenvZ(key()); + const maybe_reloaded = cache.deserAndInvalidate(env_var); + + if (maybe_reloaded) |v| { + return v; + } + + if (opts.default) |d| { + return d; + } + + return null; + } + + /// Fetch the default value of this environment variable, if any. + /// + /// It is safe to compare the result of .get() to default to test if the variable is set to + /// its default value. + pub const default: DefaultType = if (opts.default) |d| d else {}; + + fn assertPlatformSupported() void { + const missing_key_fmt = "Cannot retrieve the value of " ++ comptime_key ++ + " for {s} since no {s} key is associated with it."; + if (comptime bun.Environment.isWindows and windows_key == null) { + @compileError(std.fmt.comptimePrint(missing_key_fmt, .{ "Windows", "Windows" })); + } else if (comptime bun.Environment.isPosix and posix_key == null) { + @compileError(std.fmt.comptimePrint(missing_key_fmt, .{ "POSIX", "POSIX" })); + } + } + }; +} + +const FeatureFlagOpts = struct { + default: ?bool = false, +}; + +fn newFeatureFlag(comptime env_var: [:0]const u8, comptime opts: FeatureFlagOpts) type { + return New(kind.boolean, env_var, .{ .default = opts.default }); +} + +const bun = @import("bun"); +const std = @import("std"); diff --git a/src/feature_flags.zig b/src/feature_flags.zig index 6ee4597fe9..86deb2be6c 100644 --- a/src/feature_flags.zig +++ b/src/feature_flags.zig @@ -1,48 +1,5 @@ -/// All runtime feature flags that can be toggled with an environment variable. -/// The field names correspond exactly to the expected environment variable names. -pub const RuntimeFeatureFlag = enum { - BUN_ASSUME_PERFECT_INCREMENTAL, - BUN_BE_BUN, - BUN_DEBUG_NO_DUMP, - BUN_DESTRUCT_VM_ON_EXIT, - BUN_DISABLE_SLOW_LIFECYCLE_SCRIPT_LOGGING, - BUN_DISABLE_SOURCE_CODE_PREVIEW, - BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW, - BUN_DUMP_STATE_ON_CRASH, - BUN_ENABLE_EXPERIMENTAL_SHELL_BUILTINS, - BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG, - BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER, - BUN_FEATURE_FLAG_DISABLE_DNS_CACHE, - BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO, - BUN_FEATURE_FLAG_DISABLE_INSTALL_INDEX, - BUN_FEATURE_FLAG_DISABLE_IO_POOL, - BUN_FEATURE_FLAG_DISABLE_IPV4, - BUN_FEATURE_FLAG_DISABLE_IPV6, - BUN_FEATURE_FLAG_DISABLE_REDIS_AUTO_PIPELINING, - BUN_FEATURE_FLAG_DISABLE_RWF_NONBLOCK, - BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS, - BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH, - BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING, - BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE, - BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE, - BUN_FEATURE_FLAG_FORCE_IO_POOL, - BUN_FEATURE_FLAG_FORCE_WINDOWS_JUNCTIONS, - BUN_FEATURE_FLAG_LAST_MODIFIED_PRETEND_304, - BUN_FEATURE_FLAG_NO_LIBDEFLATE, - BUN_INSTRUMENTS, - BUN_INTERNAL_BUNX_INSTALL, - /// Suppress crash reporting and creating a core dump when we abort due to an unsupported libuv function being called - BUN_INTERNAL_SUPPRESS_CRASH_ON_UV_STUB, - /// Suppress crash reporting and creating a core dump when we abort due to a fatal Node-API error - BUN_INTERNAL_SUPPRESS_CRASH_ON_NAPI_ABORT, - /// Suppress crash reporting and creating a core dump when `process._kill()` is passed its own PID - BUN_INTERNAL_SUPPRESS_CRASH_ON_PROCESS_KILL_SELF, - /// Suppress crash reporting and creating a core dump when we abort due to a signal in `bun run` - BUN_INTERNAL_SUPPRESS_CRASH_IN_BUN_RUN, - BUN_NO_CODESIGN_MACHO_BINARY, - BUN_TRACE, - NODE_NO_WARNINGS, -}; +//! If you are adding feature-flags to this file, you are in the wrong spot. Go to env_var.zig +//! instead. /// Enable breaking changes for the next major release of Bun // TODO: Make this a CLI flag / runtime var so that we can verify disabled code paths can compile @@ -52,8 +9,6 @@ pub const breaking_changes_1_4 = false; /// This was a ~5% performance improvement pub const store_file_descriptors = !env.isBrowser; -pub const jsx_runtime_is_cjs = true; - pub const tracing = true; pub const css_supports_fence = true; @@ -68,16 +23,8 @@ pub const watch_directories = true; // This feature flag exists so when you have defines inside package.json, you can use single quotes in nested strings. pub const allow_json_single_quotes = true; -pub const react_specific_warnings = true; - pub const is_macro_enabled = !env.isWasm and !env.isWasi; -// pretend everything is always the macro environment -// useful for debugging the macro's JSX transform -pub const force_macro = false; - -pub const include_filename_in_jsx = false; - pub const disable_compression_in_http_client = false; pub const enable_keepalive = true; @@ -172,13 +119,6 @@ pub const runtime_transpiler_cache = true; /// order to isolate your bug. pub const windows_bunx_fast_path = true; -// This causes strange bugs where writing via console.log (sync) has a different -// order than via Bun.file.writer() so we turn it off until there's a unified, -// buffered writer abstraction shared throughout Bun -pub const nonblocking_stdout_and_stderr_on_posix = false; - -pub const postgresql = env.is_canary or env.isDebug; - // TODO: fix Windows-only test failures in fetch-preconnect.test.ts pub const is_fetch_preconnect_supported = env.isPosix; @@ -190,14 +130,14 @@ pub fn isLibdeflateEnabled() bool { return false; } - return !bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_NO_LIBDEFLATE); + return !bun.feature_flag.BUN_FEATURE_FLAG_NO_LIBDEFLATE.get(); } /// Enable the "app" option in Bun.serve. This option will likely be removed /// in favor of HTML loaders and configuring framework options in bunfig.toml pub fn bake() bool { // In canary or if an environment variable is specified. - return env.is_canary or env.isDebug or bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE); + return env.is_canary or env.isDebug or bun.feature_flag.BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE.get(); } /// Additional debugging features for bake.DevServer, such as the incremental visualizer. diff --git a/src/fs.zig b/src/fs.zig index 912cdbcecb..29e3a52a6f 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -536,8 +536,8 @@ pub const FileSystem = struct { return switch (Environment.os) { // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks .windows => win_tempdir_cache orelse { - const value = bun.getenvZ("TEMP") orelse bun.getenvZ("TMP") orelse brk: { - if (bun.getenvZ("SystemRoot") orelse bun.getenvZ("windir")) |windir| { + const value = bun.env_var.TEMP.get() orelse bun.env_var.TMP.get() orelse brk: { + if (bun.env_var.SYSTEMROOT.get() orelse bun.env_var.WINDIR.get()) |windir| { break :brk std.fmt.allocPrint( bun.default_allocator, "{s}\\Temp", @@ -545,7 +545,7 @@ pub const FileSystem = struct { ) catch |err| bun.handleOom(err); } - if (bun.getenvZ("USERPROFILE")) |profile| { + if (bun.env_var.HOME.get()) |profile| { var buf: bun.PathBuffer = undefined; var parts = [_]string{"AppData\\Local\\Temp"}; const out = bun.path.joinAbsStringBuf(profile, &buf, &parts, .loose); @@ -578,7 +578,7 @@ pub const FileSystem = struct { pub var tmpdir_path_set = false; pub fn tmpdirPath(_: *const @This()) []const u8 { if (!tmpdir_path_set) { - tmpdir_path = bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse platformTempDir(); + tmpdir_path = bun.env_var.BUN_TMPDIR.get() orelse platformTempDir(); tmpdir_path_set = true; } @@ -587,7 +587,7 @@ pub const FileSystem = struct { pub fn openTmpDir(_: *const RealFS) !std.fs.Dir { if (!tmpdir_path_set) { - tmpdir_path = bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse platformTempDir(); + tmpdir_path = bun.env_var.BUN_TMPDIR.get() orelse platformTempDir(); tmpdir_path_set = true; } @@ -636,7 +636,7 @@ pub const FileSystem = struct { } pub fn getDefaultTempDir() string { - return bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse platformTempDir(); + return bun.env_var.BUN_TMPDIR.get() orelse platformTempDir(); } pub fn setTempdir(path: ?string) void { diff --git a/src/http/websocket_client/WebSocketDeflate.zig b/src/http/websocket_client/WebSocketDeflate.zig index 6521669279..f03ddba3b0 100644 --- a/src/http/websocket_client/WebSocketDeflate.zig +++ b/src/http/websocket_client/WebSocketDeflate.zig @@ -129,7 +129,7 @@ pub fn deinit(self: *PerMessageDeflate) void { } fn canUseLibDeflate(len: usize) bool { - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_NO_LIBDEFLATE)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_NO_LIBDEFLATE.get()) { return false; } diff --git a/src/install/NetworkTask.zig b/src/install/NetworkTask.zig index 4401cff736..9462007e9f 100644 --- a/src/install/NetworkTask.zig +++ b/src/install/NetworkTask.zig @@ -234,7 +234,7 @@ pub fn forManifest( } // Incase the ETag causes invalidation, we fallback to the last modified date. - if (last_modified.len != 0 and bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_LAST_MODIFIED_PRETEND_304)) { + if (last_modified.len != 0 and bun.feature_flag.BUN_FEATURE_FLAG_LAST_MODIFIED_PRETEND_304.get()) { this.unsafe_http_client.client.flags.force_last_modified = true; this.unsafe_http_client.client.if_modified_since = last_modified; } diff --git a/src/install/PackageManager.zig b/src/install/PackageManager.zig index 41ebb0629f..308f1b9a22 100644 --- a/src/install/PackageManager.zig +++ b/src/install/PackageManager.zig @@ -791,7 +791,8 @@ pub fn init( try env.load(entries_option.entries, &[_][]u8{}, .production, false); initializeStore(); - if (bun.getenvZ("XDG_CONFIG_HOME") orelse bun.getenvZ(bun.DotEnv.home_env)) |data_dir| { + + if (bun.env_var.XDG_CONFIG_HOME.get() orelse bun.env_var.HOME.get()) |data_dir| { var buf: bun.PathBuffer = undefined; var parts = [_]string{ "./.npmrc", @@ -831,7 +832,7 @@ pub fn init( bun.spawn.process.WaiterThread.setShouldUseWaiterThread(); } - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_FORCE_WINDOWS_JUNCTIONS)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_FORCE_WINDOWS_JUNCTIONS.get()) { bun.sys.WindowsSymlinkOptions.has_failed_to_create_symlink = true; } diff --git a/src/install/PackageManager/PackageManagerDirectories.zig b/src/install/PackageManager/PackageManagerDirectories.zig index 3f83c43ce3..ed24ca03e3 100644 --- a/src/install/PackageManager/PackageManagerDirectories.zig +++ b/src/install/PackageManager/PackageManagerDirectories.zig @@ -165,12 +165,12 @@ pub fn fetchCacheDirectoryPath(env: *DotEnv.Loader, options: ?*const Options) Ca return CacheDir{ .path = Fs.FileSystem.instance.abs(&parts), .is_node_modules = false }; } - if (env.get("XDG_CACHE_HOME")) |dir| { + if (bun.env_var.XDG_CACHE_HOME.get()) |dir| { var parts = [_]string{ dir, ".bun/", "install/", "cache/" }; return CacheDir{ .path = Fs.FileSystem.instance.abs(&parts), .is_node_modules = false }; } - if (env.get(bun.DotEnv.home_env)) |dir| { + if (bun.env_var.HOME.get()) |dir| { var parts = [_]string{ dir, ".bun/", "install/", "cache/" }; return CacheDir{ .path = Fs.FileSystem.instance.abs(&parts), .is_node_modules = false }; } diff --git a/src/install/PackageManager/PackageManagerLifecycle.zig b/src/install/PackageManager/PackageManagerLifecycle.zig index 986a010029..3ec8dfd2c8 100644 --- a/src/install/PackageManager/PackageManagerLifecycle.zig +++ b/src/install/PackageManager/PackageManagerLifecycle.zig @@ -176,7 +176,7 @@ pub fn sleep(this: *PackageManager) void { pub fn reportSlowLifecycleScripts(this: *PackageManager) void { const log_level = this.options.log_level; if (log_level == .silent) return; - if (bun.getRuntimeFeatureFlag(.BUN_DISABLE_SLOW_LIFECYCLE_SCRIPT_LOGGING)) { + if (bun.feature_flag.BUN_DISABLE_SLOW_LIFECYCLE_SCRIPT_LOGGING.get()) { return; } diff --git a/src/install/PackageManager/PackageManagerOptions.zig b/src/install/PackageManager/PackageManagerOptions.zig index 32ae941e07..ab131760fc 100644 --- a/src/install/PackageManager/PackageManagerOptions.zig +++ b/src/install/PackageManager/PackageManagerOptions.zig @@ -171,7 +171,7 @@ pub const Update = struct { }; pub fn openGlobalDir(explicit_global_dir: string) !std.fs.Dir { - if (bun.getenvZ("BUN_INSTALL_GLOBAL_DIR")) |home_dir| { + if (bun.env_var.BUN_INSTALL_GLOBAL_DIR.get()) |home_dir| { return try std.fs.cwd().makeOpenPath(home_dir, .{}); } @@ -179,34 +179,25 @@ pub fn openGlobalDir(explicit_global_dir: string) !std.fs.Dir { return try std.fs.cwd().makeOpenPath(explicit_global_dir, .{}); } - if (bun.getenvZ("BUN_INSTALL")) |home_dir| { + if (bun.env_var.BUN_INSTALL.get()) |home_dir| { var buf: bun.PathBuffer = undefined; var parts = [_]string{ "install", "global" }; const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto); return try std.fs.cwd().makeOpenPath(path, .{}); } - if (!Environment.isWindows) { - if (bun.getenvZ("XDG_CACHE_HOME") orelse bun.getenvZ("HOME")) |home_dir| { - var buf: bun.PathBuffer = undefined; - var parts = [_]string{ ".bun", "install", "global" }; - const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto); - return try std.fs.cwd().makeOpenPath(path, .{}); - } - } else { - if (bun.getenvZ("USERPROFILE")) |home_dir| { - var buf: bun.PathBuffer = undefined; - var parts = [_]string{ ".bun", "install", "global" }; - const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto); - return try std.fs.cwd().makeOpenPath(path, .{}); - } + if (bun.env_var.XDG_CACHE_HOME.get() orelse bun.env_var.HOME.get()) |home_dir| { + var buf: bun.PathBuffer = undefined; + var parts = [_]string{ ".bun", "install", "global" }; + const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto); + return try std.fs.cwd().makeOpenPath(path, .{}); } return error.@"No global directory found"; } pub fn openGlobalBinDir(opts_: ?*const Api.BunInstall) !std.fs.Dir { - if (bun.getenvZ("BUN_INSTALL_BIN")) |home_dir| { + if (bun.env_var.BUN_INSTALL_BIN.get()) |home_dir| { return try std.fs.cwd().makeOpenPath(home_dir, .{}); } @@ -218,7 +209,7 @@ pub fn openGlobalBinDir(opts_: ?*const Api.BunInstall) !std.fs.Dir { } } - if (bun.getenvZ("BUN_INSTALL")) |home_dir| { + if (bun.env_var.BUN_INSTALL.get()) |home_dir| { var buf: bun.PathBuffer = undefined; var parts = [_]string{ "bin", @@ -227,7 +218,7 @@ pub fn openGlobalBinDir(opts_: ?*const Api.BunInstall) !std.fs.Dir { return try std.fs.cwd().makeOpenPath(path, .{}); } - if (bun.getenvZ("XDG_CACHE_HOME") orelse bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + if (bun.env_var.XDG_CACHE_HOME.get() orelse bun.env_var.HOME.get()) |home_dir| { var buf: bun.PathBuffer = undefined; var parts = [_]string{ ".bun", @@ -751,7 +742,6 @@ const std = @import("std"); const bun = @import("bun"); const DotEnv = bun.DotEnv; -const Environment = bun.Environment; const FD = bun.FD; const OOM = bun.OOM; const Output = bun.Output; diff --git a/src/install/PackageManager/patchPackage.zig b/src/install/PackageManager/patchPackage.zig index 6221283813..706c1e4418 100644 --- a/src/install/PackageManager/patchPackage.zig +++ b/src/install/PackageManager/patchPackage.zig @@ -320,7 +320,7 @@ pub fn doPatchCommit( }, }; var gitbuf: bun.PathBuffer = undefined; - const git = bun.which(&gitbuf, bun.getenvZ("PATH") orelse "", cwd, "git") orelse { + const git = bun.which(&gitbuf, bun.env_var.PATH.get() orelse "", cwd, "git") orelse { Output.prettyError( "error: git must be installed to use `bun patch --commit` \n", .{}, diff --git a/src/install/PackageManager/updatePackageJSONAndInstall.zig b/src/install/PackageManager/updatePackageJSONAndInstall.zig index f2932e0349..8f1e9815ff 100644 --- a/src/install/PackageManager/updatePackageJSONAndInstall.zig +++ b/src/install/PackageManager/updatePackageJSONAndInstall.zig @@ -569,7 +569,7 @@ fn updatePackageJSONAndInstallAndCLI( if (manager.options.global) { if (manager.options.bin_path.len > 0 and manager.track_installed_bin == .basename) { var path_buf: bun.PathBuffer = undefined; - const needs_to_print = if (bun.getenvZ("PATH")) |PATH| + const needs_to_print = if (bun.env_var.PATH.get()) |PATH| // This is not perfect // // If you already have a different binary of the same @@ -667,7 +667,7 @@ fn updatePackageJSONAndInstallAndCLI( , .{ bun.fmt.quote(manager.track_installed_bin.basename), - MoreInstructions{ .shell = bun.cli.ShellCompletions.Shell.fromEnv([]const u8, bun.getenvZ("SHELL") orelse ""), .folder = manager.options.bin_path }, + MoreInstructions{ .shell = bun.cli.ShellCompletions.Shell.fromEnv([]const u8, bun.env_var.SHELL.platformGet() orelse ""), .folder = manager.options.bin_path }, }, ); Output.flush(); diff --git a/src/install/extract_tarball.zig b/src/install/extract_tarball.zig index 5770c5d4f9..c0e98e3e66 100644 --- a/src/install/extract_tarball.zig +++ b/src/install/extract_tarball.zig @@ -495,7 +495,7 @@ fn extract(this: *const ExtractTarball, log: *logger.Log, tgz_bytes: []const u8) }; } - if (!bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_INSTALL_INDEX)) { + if (!bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_INSTALL_INDEX.get()) { // create an index storing each version of a package installed if (strings.indexOfChar(basename, '/') == null) create_index: { const dest_name = switch (this.resolution.tag) { diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 3f4a5c7313..38679c7c3e 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -313,7 +313,7 @@ pub fn loadFromDir( switch (result) { .ok => { - if (bun.getenvZ("BUN_DEBUG_TEST_TEXT_LOCKFILE") != null and manager != null) { + if (bun.env_var.BUN_DEBUG_TEST_TEXT_LOCKFILE.get() and manager != null) { // Convert the loaded binary lockfile into a text lockfile in memory, then // parse it back into a binary lockfile. diff --git a/src/install/repository.zig b/src/install/repository.zig index 59feeef8b6..0341f46420 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -16,21 +16,10 @@ const SloppyGlobalGitConfig = struct { } pub fn loadAndParse() void { - const home_dir_path = brk: { - if (comptime Environment.isWindows) { - if (bun.getenvZ("USERPROFILE")) |env| - break :brk env; - } else { - if (bun.getenvZ("HOME")) |env| - break :brk env; - } - - // won't find anything - return; - }; + const home_dir = bun.env_var.HOME.get() orelse return; var config_file_path_buf: bun.PathBuffer = undefined; - const config_file_path = bun.path.joinAbsStringBufZ(home_dir_path, &config_file_path_buf, &.{".gitconfig"}, .auto); + const config_file_path = bun.path.joinAbsStringBufZ(home_dir, &config_file_path_buf, &.{".gitconfig"}, .auto); var stack_fallback = std.heap.stackFallback(4096, bun.default_allocator); const allocator = stack_fallback.get(); const source = File.toSource(config_file_path, allocator, .{ .convert_bom = true }).unwrap() catch { diff --git a/src/interchange/yaml.zig b/src/interchange/yaml.zig index 947307c874..5a899df9d4 100644 --- a/src/interchange/yaml.zig +++ b/src/interchange/yaml.zig @@ -4758,7 +4758,7 @@ pub fn Parser(comptime enc: Encoding) type { return this.str.len(); } - pub fn done(self: *const @This()) String { + pub fn done(self: *@This()) String { self.parser.whitespace_buf.clearRetainingCapacity(); return self.str; } diff --git a/src/linux.zig b/src/linux.zig index afe636afe4..86623f28b7 100644 --- a/src/linux.zig +++ b/src/linux.zig @@ -45,7 +45,7 @@ pub const RWFFlagSupport = enum(u8) { if (comptime !bun.Environment.isLinux) return false; switch (rwf_bool.load(.monotonic)) { .unknown => { - if (isLinuxKernelVersionWithBuggyRWF_NONBLOCK() or bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_RWF_NONBLOCK)) { + if (isLinuxKernelVersionWithBuggyRWF_NONBLOCK() or bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_RWF_NONBLOCK.get()) { rwf_bool.store(.unsupported, .monotonic); return false; } diff --git a/src/macho.zig b/src/macho.zig index a46fbba7cc..51feef8781 100644 --- a/src/macho.zig +++ b/src/macho.zig @@ -190,7 +190,7 @@ pub const MachoFile = struct { linkedit_seg.fileoff += @as(usize, @intCast(size_diff)); linkedit_seg.vmaddr += @as(usize, @intCast(size_diff)); - if (self.header.cputype == macho.CPU_TYPE_ARM64 and !bun.getRuntimeFeatureFlag(.BUN_NO_CODESIGN_MACHO_BINARY)) { + if (self.header.cputype == macho.CPU_TYPE_ARM64 and !bun.feature_flag.BUN_NO_CODESIGN_MACHO_BINARY.get()) { // We also update the sizes of the LINKEDIT segment to account for the hashes we're adding linkedit_seg.filesize += @as(usize, @intCast(size_of_new_hashes)); linkedit_seg.vmsize += @as(usize, @intCast(size_of_new_hashes)); @@ -341,7 +341,7 @@ pub const MachoFile = struct { } pub fn buildAndSign(self: *MachoFile, writer: anytype) !void { - if (self.header.cputype == macho.CPU_TYPE_ARM64 and !bun.getRuntimeFeatureFlag(.BUN_NO_CODESIGN_MACHO_BINARY)) { + if (self.header.cputype == macho.CPU_TYPE_ARM64 and !bun.feature_flag.BUN_NO_CODESIGN_MACHO_BINARY.get()) { var data = std.ArrayList(u8).init(self.allocator); defer data.deinit(); try self.build(data.writer()); diff --git a/src/napi/napi.zig b/src/napi/napi.zig index a2109168a1..b51de870e1 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -1361,7 +1361,7 @@ pub export fn napi_internal_register_cleanup_zig(env_: napi_env) void { } pub export fn napi_internal_suppress_crash_on_abort_if_desired() void { - if (bun.getRuntimeFeatureFlag(.BUN_INTERNAL_SUPPRESS_CRASH_ON_NAPI_ABORT)) { + if (bun.feature_flag.BUN_INTERNAL_SUPPRESS_CRASH_ON_NAPI_ABORT.get()) { bun.crash_handler.suppressReporting(); } } diff --git a/src/output.zig b/src/output.zig index 6d1f79d90a..cb6ecd4a41 100644 --- a/src/output.zig +++ b/src/output.zig @@ -80,31 +80,22 @@ pub const Source = struct { } pub fn isNoColor() bool { - const no_color = bun.getenvZ("NO_COLOR") orelse return false; - // https://no-color.org/ - // "when present and not an empty string (regardless of its value)" - return no_color.len != 0; + return bun.env_var.NO_COLOR.get(); } pub fn getForceColorDepth() ?ColorDepth { - const force_color = bun.getenvZ("FORCE_COLOR") orelse return null; + const force_color = bun.env_var.FORCE_COLOR.get() orelse return null; // Supported by Node.js, if set will ignore NO_COLOR. // - "0" to indicate no color support // - "1", "true", or "" to indicate 16-color support // - "2" to indicate 256-color support // - "3" to indicate 16 million-color support - if (strings.eqlComptime(force_color, "1") or strings.eqlComptime(force_color, "true") or strings.eqlComptime(force_color, "")) { - return ColorDepth.@"16"; - } - - if (strings.eqlComptime(force_color, "2")) { - return ColorDepth.@"256"; - } - if (strings.eqlComptime(force_color, "3")) { - return ColorDepth.@"16m"; - } - - return ColorDepth.none; + return switch (force_color) { + 0 => .none, + 1 => .@"16", + 2 => .@"256", + else => .@"16m", + }; } pub fn isForceColor() bool { @@ -273,29 +264,22 @@ pub const Source = struct { return; } - const term = bun.getenvZ("TERM") orelse ""; + const term = bun.env_var.TERM.get() orelse ""; if (strings.eqlComptime(term, "dumb")) { return; } - if (bun.getenvZ("TMUX") != null) { + if (bun.env_var.TMUX.get() != null) { lazy_color_depth = .@"256"; return; } - if (bun.getenvZ("CI")) |ci| { - inline for (.{ "APPVEYOR", "BUILDKITE", "CIRCLECI", "DRONE", "GITHUB_ACTIONS", "GITLAB_CI", "TRAVIS" }) |ci_env| { - if (strings.eqlComptime(ci, ci_env)) { - lazy_color_depth = .@"256"; - return; - } - } - + if (bun.env_var.CI.get() != null) { lazy_color_depth = .@"16"; return; } - if (bun.getenvZ("TERM_PROGRAM")) |term_program| { + if (bun.env_var.TERM_PROGRAM.get()) |term_program| { const use_16m = .{ "ghostty", "MacTerm", @@ -313,7 +297,7 @@ pub const Source = struct { var has_color_term_set = false; - if (bun.getenvZ("COLORTERM")) |color_term| { + if (bun.env_var.COLORTERM.get()) |color_term| { if (strings.eqlComptime(color_term, "truecolor") or strings.eqlComptime(color_term, "24bit")) { lazy_color_depth = .@"16m"; return; @@ -450,10 +434,9 @@ pub inline fn isEmojiEnabled() bool { } pub fn isGithubAction() bool { - if (bun.getenvZ("GITHUB_ACTIONS")) |value| { - return strings.eqlComptime(value, "true") and - // Do not print github annotations for AI agents because that wastes the context window. - !isAIAgent(); + if (bun.env_var.GITHUB_ACTIONS.get()) { + // Do not print github annotations for AI agents because that wastes the context window. + return !isAIAgent(); } return false; } @@ -462,7 +445,7 @@ pub fn isAIAgent() bool { const get_is_agent = struct { var value = false; fn evaluate() bool { - if (bun.getenvZ("AGENT")) |env| { + if (bun.env_var.AGENT.get()) |env| { return strings.eqlComptime(env, "1"); } @@ -471,12 +454,12 @@ pub fn isAIAgent() bool { } // Claude Code. - if (bun.getenvTruthy("CLAUDECODE")) { + if (bun.env_var.CLAUDECODE.get()) { return true; } // Replit. - if (bun.getenvTruthy("REPL_ID")) { + if (bun.env_var.REPL_ID.get()) { return true; } @@ -509,12 +492,7 @@ pub fn isAIAgent() bool { pub fn isVerbose() bool { // Set by Github Actions when a workflow is run using debug mode. - if (bun.getenvZ("RUNNER_DEBUG")) |value| { - if (strings.eqlComptime(value, "1")) { - return true; - } - } - return false; + return bun.env_var.RUNNER_DEBUG.get(); } pub fn enableBuffering() void { @@ -826,10 +804,10 @@ fn ScopedLogger(comptime tagname: []const u8, comptime visibility: Visibility) t fn evaluateIsVisible() void { if (bun.getenvZAnyCase("BUN_DEBUG_" ++ tagname)) |val| { really_disable.store(strings.eqlComptime(val, "0"), .monotonic); - } else if (bun.getenvZAnyCase("BUN_DEBUG_ALL")) |val| { - really_disable.store(strings.eqlComptime(val, "0"), .monotonic); - } else if (bun.getenvZAnyCase("BUN_DEBUG_QUIET_LOGS")) |val| { - really_disable.store(really_disable.load(.monotonic) or !strings.eqlComptime(val, "0"), .monotonic); + } else if (bun.env_var.BUN_DEBUG_ALL.get()) |val| { + really_disable.store(val, .monotonic); + } else if (bun.env_var.BUN_DEBUG_QUIET_LOGS.get()) |val| { + really_disable.store(really_disable.load(.monotonic) or !val, .monotonic); } else { for (bun.argv) |arg| { if (strings.eqlCaseInsensitiveASCII(arg, comptime "--debug-" ++ tagname, true)) { @@ -1266,7 +1244,7 @@ extern "c" fn getpid() c_int; pub fn initScopedDebugWriterAtStartup() void { bun.debugAssert(source_set); - if (bun.getenvZ("BUN_DEBUG")) |path| { + if (bun.env_var.BUN_DEBUG.get()) |path| { if (path.len > 0 and !strings.eql(path, "0") and !strings.eql(path, "false")) { if (std.fs.path.dirname(path)) |dir| { std.fs.cwd().makePath(dir) catch {}; diff --git a/src/patch.zig b/src/patch.zig index 2143a8b4f8..95923d05ad 100644 --- a/src/patch.zig +++ b/src/patch.zig @@ -1267,7 +1267,7 @@ pub fn spawnOpts( "XDG_CONFIG_HOME", "USERPROFILE", }; - const PATH = bun.getenvZ("PATH"); + const PATH = bun.env_var.PATH.get(); const envp_buf = bun.handleOom(bun.default_allocator.allocSentinel(?[*:0]const u8, env_arr.len + @as(usize, if (PATH != null) 1 else 0), null)); for (0..env_arr.len) |i| { envp_buf[i] = env_arr[i].ptr; @@ -1392,7 +1392,7 @@ pub fn gitDiffInternal( child_proc.stderr_behavior = .Pipe; var map = std.process.EnvMap.init(allocator); defer map.deinit(); - if (bun.getenvZ("PATH")) |v| try map.put("PATH", v); + if (bun.env_var.PATH.get()) |v| try map.put("PATH", v); try map.put("GIT_CONFIG_NOSYSTEM", "1"); try map.put("HOME", ""); try map.put("XDG_CONFIG_HOME", ""); diff --git a/src/perf.zig b/src/perf.zig index 04a608040f..ba270fabe9 100644 --- a/src/perf.zig +++ b/src/perf.zig @@ -19,13 +19,13 @@ pub const Ctx = union(enum) { var is_enabled_once = std.once(isEnabledOnce); var is_enabled = std.atomic.Value(bool).init(false); fn isEnabledOnMacOSOnce() void { - if (bun.getenvZ("DYLD_ROOT_PATH") != null or bun.getRuntimeFeatureFlag(.BUN_INSTRUMENTS)) { + if (bun.env_var.DYLD_ROOT_PATH.get() != null or bun.feature_flag.BUN_INSTRUMENTS.get()) { is_enabled.store(true, .seq_cst); } } fn isEnabledOnLinuxOnce() void { - if (bun.getRuntimeFeatureFlag(.BUN_TRACE)) { + if (bun.feature_flag.BUN_TRACE.get()) { is_enabled.store(true, .seq_cst); } } diff --git a/src/shell/Builtin.zig b/src/shell/Builtin.zig index 8578485590..aaa5671c8d 100644 --- a/src/shell/Builtin.zig +++ b/src/shell/Builtin.zig @@ -112,7 +112,7 @@ pub const Kind = enum { } fn forceEnableOnPosix() bool { - return bun.getRuntimeFeatureFlag(.BUN_ENABLE_EXPERIMENTAL_SHELL_BUILTINS); + return bun.feature_flag.BUN_ENABLE_EXPERIMENTAL_SHELL_BUILTINS.get(); } pub fn fromStr(str: []const u8) ?Builtin.Kind { diff --git a/src/sql/mysql/MySQLRequestQueue.zig b/src/sql/mysql/MySQLRequestQueue.zig index 15d87a306d..1606e70637 100644 --- a/src/sql/mysql/MySQLRequestQueue.zig +++ b/src/sql/mysql/MySQLRequestQueue.zig @@ -32,7 +32,7 @@ pub inline fn markAsPrepared(this: *@This()) void { } } pub inline fn canPipeline(this: *@This(), connection: *MySQLConnection) bool { - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING.get()) { @branchHint(.unlikely); return false; } diff --git a/src/sql/postgres/DebugSocketMonitorReader.zig b/src/sql/postgres/DebugSocketMonitorReader.zig index 1af82ce043..d8444ffd89 100644 --- a/src/sql/postgres/DebugSocketMonitorReader.zig +++ b/src/sql/postgres/DebugSocketMonitorReader.zig @@ -3,7 +3,7 @@ pub var enabled = false; pub var check = std.once(load); pub fn load() void { - if (bun.getenvZAnyCase("BUN_POSTGRES_SOCKET_MONITOR_READER")) |monitor| { + if (bun.env_var.BUN_POSTGRES_SOCKET_MONITOR_READER.get()) |monitor| { enabled = true; file = std.fs.cwd().createFile(monitor, .{ .truncate = true }) catch { enabled = false; diff --git a/src/sql/postgres/DebugSocketMonitorWriter.zig b/src/sql/postgres/DebugSocketMonitorWriter.zig index c721cdd2ac..8301d17a2b 100644 --- a/src/sql/postgres/DebugSocketMonitorWriter.zig +++ b/src/sql/postgres/DebugSocketMonitorWriter.zig @@ -7,7 +7,7 @@ pub fn write(data: []const u8) void { } pub fn load() void { - if (bun.getenvZAnyCase("BUN_POSTGRES_SOCKET_MONITOR")) |monitor| { + if (bun.env_var.BUN_POSTGRES_SOCKET_MONITOR.get()) |monitor| { enabled = true; file = std.fs.cwd().createFile(monitor, .{ .truncate = true }) catch { enabled = false; diff --git a/src/sql/postgres/PostgresSQLConnection.zig b/src/sql/postgres/PostgresSQLConnection.zig index 4f4787de42..6ed3e6c030 100644 --- a/src/sql/postgres/PostgresSQLConnection.zig +++ b/src/sql/postgres/PostgresSQLConnection.zig @@ -984,7 +984,7 @@ pub fn hasQueryRunning(this: *PostgresSQLConnection) bool { } pub fn canPipeline(this: *PostgresSQLConnection) bool { - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING.get()) { @branchHint(.unlikely); return false; } diff --git a/src/tracy.zig b/src/tracy.zig index 9963250c04..475bcf8a8d 100644 --- a/src/tracy.zig +++ b/src/tracy.zig @@ -528,7 +528,7 @@ fn dlsym(comptime Type: type, comptime symbol: [:0]const u8) ?Type { const RLTD: std.c.RTLD = if (bun.Environment.isMac) @bitCast(@as(i32, -2)) else if (bun.Environment.isLinux) .{} else {}; - if (bun.getenvZ("BUN_TRACY_PATH")) |path| { + if (bun.env_var.BUN_TRACY_PATH.get()) |path| { const handle = bun.sys.dlopen(&(std.posix.toPosixPath(path) catch unreachable), RLTD); if (handle != null) { Handle.handle = handle; diff --git a/src/transpiler.zig b/src/transpiler.zig index 668ee1a978..2ea1d4df14 100644 --- a/src/transpiler.zig +++ b/src/transpiler.zig @@ -902,7 +902,7 @@ pub const Transpiler = struct { comptime format: js_printer.Format, handler: js_printer.SourceMapHandler, ) !usize { - if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS)) { + if (bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS.get()) { return transpiler.printWithSourceMapMaybe( result.ast, &result.source, diff --git a/src/valkey/js_valkey.zig b/src/valkey/js_valkey.zig index bb24dbc771..02ad4e9ef3 100644 --- a/src/valkey/js_valkey.zig +++ b/src/valkey/js_valkey.zig @@ -1583,7 +1583,7 @@ fn SocketHandler(comptime ssl: bool) type { const Options = struct { pub fn fromJS(globalObject: *jsc.JSGlobalObject, options_obj: jsc.JSValue) !valkey.Options { var this = valkey.Options{ - .enable_auto_pipelining = !bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_REDIS_AUTO_PIPELINING), + .enable_auto_pipelining = !bun.feature_flag.BUN_FEATURE_FLAG_DISABLE_REDIS_AUTO_PIPELINING.get(), }; if (try options_obj.getOptionalInt(globalObject, "idleTimeout", u32)) |idle_timeout| { diff --git a/src/watcher/INotifyWatcher.zig b/src/watcher/INotifyWatcher.zig index b4996fa5b2..96d79fa681 100644 --- a/src/watcher/INotifyWatcher.zig +++ b/src/watcher/INotifyWatcher.zig @@ -94,9 +94,7 @@ pub fn init(this: *INotifyWatcher, _: []const u8) !void { bun.assert(!this.loaded); this.loaded = true; - if (bun.getenvZ("BUN_INOTIFY_COALESCE_INTERVAL")) |env| { - this.coalesce_interval = std.fmt.parseInt(isize, env, 10) catch 100_000; - } + this.coalesce_interval = std.math.cast(isize, bun.env_var.BUN_INOTIFY_COALESCE_INTERVAL.get()) orelse 100_000; // TODO: convert to bun.sys.Error this.fd = .fromNative(try std.posix.inotify_init1(IN.CLOEXEC)); diff --git a/src/watcher/WatcherTrace.zig b/src/watcher/WatcherTrace.zig index d2beeb1e4e..cd01ba2969 100644 --- a/src/watcher/WatcherTrace.zig +++ b/src/watcher/WatcherTrace.zig @@ -6,7 +6,7 @@ var trace_file: ?bun.sys.File = null; pub fn init() void { if (trace_file != null) return; - if (bun.getenvZ("BUN_WATCHER_TRACE")) |trace_path| { + if (bun.env_var.BUN_WATCHER_TRACE.get()) |trace_path| { if (trace_path.len > 0) { const flags = bun.O.WRONLY | bun.O.CREAT | bun.O.APPEND; const mode = 0o644; diff --git a/test/internal/ban-limits.json b/test/internal/ban-limits.json index 9edd2b793a..7cac923618 100644 --- a/test/internal/ban-limits.json +++ b/test/internal/ban-limits.json @@ -36,7 +36,7 @@ "std.enums.tagName(": 2, "std.fs.Dir": 164, "std.fs.File": 62, - "std.fs.cwd": 103, + "std.fs.cwd": 102, "std.log": 1, "std.mem.indexOfAny(u8": 0, "std.unicode": 27,