diff --git a/docs/runtime/bunfig.md b/docs/runtime/bunfig.md index ca0a36f7eb..47fbebd4d4 100644 --- a/docs/runtime/bunfig.md +++ b/docs/runtime/bunfig.md @@ -109,6 +109,19 @@ The `telemetry` field permit to enable/disable the analytics records. Bun record telemetry = false ``` +### `dotenv` + +Control whether Bun automatically loads `.env` files. Default `true`. + +```toml +# Disable automatic .env file loading +dotenv = false +``` + +When set to `false`, Bun will not automatically load `.env`, `.env.local`, `.env.production`, `.env.development`, or `.env.test` files at runtime. Environment variables set in your shell or passed to the process will still be available via `process.env`. + +See [Runtime > Environment variables](/docs/runtime/env#disabling-automatic-env-file-loading) for more details. + ### `console` Configure console output behavior. diff --git a/docs/runtime/env.md b/docs/runtime/env.md index 78da1e94d1..6de165ddd5 100644 --- a/docs/runtime/env.md +++ b/docs/runtime/env.md @@ -69,6 +69,20 @@ $ bun --env-file=.env.1 src/index.ts $ bun --env-file=.env.abc --env-file=.env.def run build ``` +### Disabling automatic `.env` file loading + +You can disable Bun's automatic `.env` file loading by setting `dotenv = false` in your [`bunfig.toml`](/docs/runtime/bunfig) configuration file. + +```toml#bunfig.toml +dotenv = false +``` + +When disabled, Bun will not automatically load `.env`, `.env.local`, `.env.production`, `.env.development`, or `.env.test` files. However, environment variables set in your shell or passed to the process will still be available via `process.env`. + +{% callout %} +**Note:** When `dotenv = false`, you can still manually load specific `.env` files using the `--env-file` flag. +{% /callout %} + ### Quotation marks Bun supports double quotes, single quotes, and template literal backticks: diff --git a/src/api/schema.zig b/src/api/schema.zig index f4f70201b9..2022c025de 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -1734,6 +1734,9 @@ pub const api = struct { /// from --unhandled-rejections, default is 'bun' unhandled_rejections: ?UnhandledRejections = null, + /// from bunfig.toml dotenv field, disables .env file loading when true + disable_dotenv: bool = false, + bunfig_path: []const u8, pub fn decode(reader: anytype) anyerror!TransformOptions { diff --git a/src/bunfig.zig b/src/bunfig.zig index 2041454161..c326e15046 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -779,6 +779,18 @@ pub const Bunfig = struct { } } } + + if (json.get("dotenv")) |dotenv_expr| { + if (dotenv_expr.asBool()) |value| { + // When dotenv is set to false, disable .env loading + // When true or not set, keep the default behavior + if (!value) { + this.bunfig.disable_dotenv = true; + } + } else { + try this.addError(dotenv_expr.loc, "Expected boolean"); + } + } } if (json.getObject("serve")) |serve_obj2| { diff --git a/src/transpiler.zig b/src/transpiler.zig index 668ee1a978..f2cd2c1b0f 100644 --- a/src/transpiler.zig +++ b/src/transpiler.zig @@ -474,6 +474,17 @@ pub const Transpiler = struct { } pub fn runEnvLoader(this: *Transpiler, skip_default_env: bool) !void { + // Check if dotenv is disabled via bunfig.toml + if (this.options.transform_options.disable_dotenv) { + // Skip .env loading but still load from process.env + try this.env.loadProcess(); + if (this.env.isProduction()) { + this.options.setProduction(true); + this.resolver.opts.setProduction(true); + } + return; + } + switch (this.options.env.behavior) { .prefix, .load_all, .load_all_without_inlining => { // Step 1. Load the project root. diff --git a/test/cli/run/env.test.ts b/test/cli/run/env.test.ts index 295c0e0056..1a4fd2b2ce 100644 --- a/test/cli/run/env.test.ts +++ b/test/cli/run/env.test.ts @@ -857,3 +857,45 @@ LARGE3="${"c".repeat(3000)}" expect(stdout).toBe("4096"); }); }); + +describe("bunfig.toml dotenv flag", () => { + test("dotenv = false disables .env loading", () => { + const dir = tempDirWithFiles("bunfig-dotenv-disable", { + ".env": "FOO=from_env\n", + "bunfig.toml": "dotenv = false\n", + "index.ts": "console.log(process.env.FOO || 'undefined');", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("undefined"); + }); + + test("dotenv = false still allows process.env to work", () => { + const dir = tempDirWithFiles("bunfig-dotenv-disable-processenv", { + ".env": "FOO=from_env\n", + "bunfig.toml": "dotenv = false\n", + "index.ts": "console.log(process.env.BAR || 'undefined');", + }); + const { stdout } = bunRun(`${dir}/index.ts`, { BAR: "from_process" }); + expect(stdout).toBe("from_process"); + }); + + test("dotenv = true allows .env loading (explicit)", () => { + const dir = tempDirWithFiles("bunfig-dotenv-enable", { + ".env": "FOO=from_env\n", + "bunfig.toml": "dotenv = true\n", + "index.ts": "console.log(process.env.FOO);", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("from_env"); + }); + + test("without bunfig.toml dotenv field, .env loads by default", () => { + const dir = tempDirWithFiles("bunfig-dotenv-default", { + ".env": "FOO=from_env\n", + "bunfig.toml": "# no dotenv field\n", + "index.ts": "console.log(process.env.FOO);", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("from_env"); + }); +});