diff --git a/docs/runtime/env.md b/docs/runtime/env.md index 78da1e94d1..e075846803 100644 --- a/docs/runtime/env.md +++ b/docs/runtime/env.md @@ -225,6 +225,11 @@ These environment variables are read by Bun and configure aspects of its behavio - `BUN_OPTIONS` - Prepends command-line arguments to any Bun execution. For example, `BUN_OPTIONS="--hot"` makes `bun run dev` behave like `bun --hot run dev`. +--- + +- `NODE_OPTIONS` +- Works identically to `BUN_OPTIONS`. Prepends command-line arguments to any Bun execution. If both `BUN_OPTIONS` and `NODE_OPTIONS` are set, `BUN_OPTIONS` takes precedence. This provides compatibility with Node.js tooling. + {% /table %} ## Runtime transpiler caching diff --git a/src/bun.zig b/src/bun.zig index fb4b98ba84..288947ea3d 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -2208,7 +2208,7 @@ pub fn initArgv(allocator: std.mem.Allocator) !void { argv = try std.process.argsAlloc(allocator); } - if (bun.env_var.BUN_OPTIONS.get()) |opts| { + if (bun.env_var.BUN_OPTIONS.get() orelse bun.env_var.NODE_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/env_var.zig b/src/env_var.zig index cdd75eda12..3669d35e57 100644 --- a/src/env_var.zig +++ b/src/env_var.zig @@ -99,6 +99,7 @@ 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_OPTIONS = New(kind.string, "NODE_OPTIONS", .{}); 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", .{}); diff --git a/test/cli/env/bun-options.test.ts b/test/cli/env/bun-options.test.ts index f9b2774dc9..6a25956ed9 100644 --- a/test/cli/env/bun-options.test.ts +++ b/test/cli/env/bun-options.test.ts @@ -69,3 +69,60 @@ describe("BUN_OPTIONS environment variable", () => { expect(result.stdout.toString()).toContain("NORMAL"); }); }); + +describe("NODE_OPTIONS environment variable", () => { + test("basic usage - passes options to bun command", () => { + const result = spawnSync({ + cmd: [bunExe()], + env: { + ...bunEnv, + NODE_OPTIONS: "--print='NODE_OPTIONS WAS A SUCCESS'", + }, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout.toString()).toContain("NODE_OPTIONS WAS A SUCCESS"); + }); + + test("multiple options - passes all options to bun command", () => { + const result = spawnSync({ + cmd: [bunExe()], + env: { + ...bunEnv, + NODE_OPTIONS: "--print='MULTIPLE OPTIONS' --quiet", + }, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout.toString()).toContain("MULTIPLE OPTIONS"); + }); + + test("BUN_OPTIONS takes precedence over NODE_OPTIONS", () => { + const result = spawnSync({ + cmd: [bunExe()], + env: { + ...bunEnv, + NODE_OPTIONS: "--print='NODE'", + BUN_OPTIONS: "--print='BUN'", + }, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout.toString()).toContain("BUN"); + expect(result.stdout.toString()).not.toContain("NODE"); + }); + + test("NODE_OPTIONS works when BUN_OPTIONS is not set", () => { + const result = spawnSync({ + cmd: [bunExe()], + env: { + ...bunEnv, + NODE_OPTIONS: "--print='FALLBACK TO NODE_OPTIONS'", + BUN_OPTIONS: undefined, + }, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout.toString()).toContain("FALLBACK TO NODE_OPTIONS"); + }); +});