mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 13:51:47 +00:00
Compare commits
7 Commits
claude/fix
...
claude/no-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81b8bbae0b | ||
|
|
cfe8879d40 | ||
|
|
3327e9b8c3 | ||
|
|
5ec48f3d4b | ||
|
|
a53ac1f152 | ||
|
|
5dd4be7e40 | ||
|
|
34e69f4be7 |
@@ -109,6 +109,15 @@ The `telemetry` field permit to enable/disable the analytics records. Bun record
|
||||
telemetry = false
|
||||
```
|
||||
|
||||
### `env.file`
|
||||
|
||||
Disable automatic `.env` file loading.
|
||||
|
||||
```toml
|
||||
[env]
|
||||
file = false
|
||||
```
|
||||
|
||||
### `console`
|
||||
|
||||
Configure console output behavior.
|
||||
|
||||
@@ -69,6 +69,21 @@ $ bun --env-file=.env.1 src/index.ts
|
||||
$ bun --env-file=.env.abc --env-file=.env.def run build
|
||||
```
|
||||
|
||||
### Disabling `.env` files
|
||||
|
||||
Use `--no-env-file` to disable loading `.env` files:
|
||||
|
||||
```sh
|
||||
$ bun --no-env-file src/index.ts
|
||||
```
|
||||
|
||||
Or in bunfig.toml:
|
||||
|
||||
```toml
|
||||
[env]
|
||||
file = false
|
||||
```
|
||||
|
||||
### Quotation marks
|
||||
|
||||
Bun supports double quotes, single quotes, and template literal backticks:
|
||||
|
||||
@@ -72,6 +72,7 @@ Several Bun CLI flags can be used with `bun test` to modify its behavior:
|
||||
- `--tsconfig-override`: Uses a different tsconfig
|
||||
- `--conditions`: Sets package.json conditions for module resolution
|
||||
- `--env-file`: Loads environment variables for tests
|
||||
- `--no-env-file`: Disables loading of `.env` files
|
||||
|
||||
### Installation-related Flags
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ pub const Run = struct {
|
||||
b.options.minify_identifiers = ctx.bundler_options.minify_identifiers;
|
||||
b.options.minify_whitespace = ctx.bundler_options.minify_whitespace;
|
||||
b.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
|
||||
b.options.env.behavior = ctx.bundler_options.env_behavior;
|
||||
b.options.env.prefix = ctx.bundler_options.env_prefix;
|
||||
b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers;
|
||||
b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace;
|
||||
|
||||
@@ -134,6 +136,8 @@ pub const Run = struct {
|
||||
try @import("./bun.js/config.zig").configureTransformOptionsForBunVM(ctx.allocator, ctx.args),
|
||||
null,
|
||||
);
|
||||
bundle.options.env.behavior = ctx.bundler_options.env_behavior;
|
||||
bundle.options.env.prefix = ctx.bundler_options.env_prefix;
|
||||
try bundle.runEnvLoader(false);
|
||||
const mini = jsc.MiniEventLoop.initGlobal(bundle.env);
|
||||
mini.top_level_dir = ctx.args.absolute_working_dir orelse "";
|
||||
@@ -210,10 +214,10 @@ pub const Run = struct {
|
||||
b.options.minify_identifiers = ctx.bundler_options.minify_identifiers;
|
||||
b.options.minify_whitespace = ctx.bundler_options.minify_whitespace;
|
||||
b.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
|
||||
b.options.env.behavior = ctx.bundler_options.env_behavior;
|
||||
b.options.env.prefix = ctx.bundler_options.env_prefix;
|
||||
b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers;
|
||||
b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace;
|
||||
|
||||
b.options.env.behavior = .load_all_without_inlining;
|
||||
// b.options.minify_syntax = ctx.bundler_options.minify_syntax;
|
||||
|
||||
switch (ctx.debug.macros) {
|
||||
|
||||
@@ -191,6 +191,25 @@ pub const Bunfig = struct {
|
||||
this.bunfig.origin = try expr.data.e_string.string(allocator);
|
||||
}
|
||||
|
||||
// Support for env configuration in bunfig.toml
|
||||
if (json.get("env")) |env_expr| {
|
||||
if (env_expr.get("file")) |file_expr| {
|
||||
switch (file_expr.data) {
|
||||
.e_boolean => |boolean| {
|
||||
if (!boolean.value) {
|
||||
// env.file = false disables .env file loading
|
||||
this.ctx.bundler_options.env_behavior = api.DotEnvBehavior.disable;
|
||||
}
|
||||
},
|
||||
.e_null => {
|
||||
// env.file = null also disables .env file loading
|
||||
this.ctx.bundler_options.env_behavior = api.DotEnvBehavior.disable;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (comptime cmd == .RunCommand or cmd == .AutoCommand) {
|
||||
if (json.get("serve")) |expr| {
|
||||
if (expr.get("port")) |port| {
|
||||
|
||||
@@ -434,6 +434,13 @@ pub const Command = struct {
|
||||
};
|
||||
global_cli_ctx = &context_data;
|
||||
|
||||
// Set appropriate default env_behavior based on command
|
||||
// Build command uses .disable as default (env vars handled via define mechanism)
|
||||
// Run/Test/other commands use .load_all_without_inlining as default
|
||||
if (comptime command != .BuildCommand) {
|
||||
global_cli_ctx.bundler_options.env_behavior = .load_all_without_inlining;
|
||||
}
|
||||
|
||||
if (comptime Command.Tag.uses_global_options.get(command)) {
|
||||
global_cli_ctx.args = try Arguments.parse(allocator, global_cli_ctx, command);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ pub const ParamType = clap.Param(clap.Help);
|
||||
|
||||
pub const base_params_ = (if (Environment.show_crash_trace) debug_params else [_]ParamType{}) ++ [_]ParamType{
|
||||
clap.parseParam("--env-file <STR>... Load environment variables from the specified file(s)") catch unreachable,
|
||||
clap.parseParam("--no-env-file Disable loading of environment variables from .env files") catch unreachable,
|
||||
clap.parseParam("--cwd <STR> Absolute path to resolve files & entry points from. This just changes the process' cwd.") catch unreachable,
|
||||
clap.parseParam("-c, --config <PATH>? Specify path to Bun config file. Default <d>$cwd<r>/bunfig.toml") catch unreachable,
|
||||
clap.parseParam("-h, --help Display this menu and exit") catch unreachable,
|
||||
@@ -535,6 +536,20 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
opts.env_files = args.options("--env-file");
|
||||
opts.extension_order = args.options("--extension-order");
|
||||
|
||||
// Handle --no-env-file flag to disable .env file loading
|
||||
if (args.flag("--no-env-file")) {
|
||||
// Clear any env files to signal no env loading
|
||||
opts.env_files = &[_][]const u8{};
|
||||
// Set bundler options to disable env loading
|
||||
ctx.bundler_options.env_behavior = Api.DotEnvBehavior.disable;
|
||||
} else if (opts.env_files.len > 0) {
|
||||
// If --env-file is explicitly provided, ensure env loading is enabled
|
||||
// This overrides any bunfig.toml setting
|
||||
if (ctx.bundler_options.env_behavior == Api.DotEnvBehavior.disable) {
|
||||
ctx.bundler_options.env_behavior = Api.DotEnvBehavior.load_all_without_inlining;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.flag("--preserve-symlinks")) {
|
||||
opts.preserve_symlinks = true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ pub const ExecCommand = struct {
|
||||
try @import("../bun.js/config.zig").configureTransformOptionsForBunVM(ctx.allocator, ctx.args),
|
||||
null,
|
||||
);
|
||||
bundle.options.env.behavior = ctx.bundler_options.env_behavior;
|
||||
bundle.options.env.prefix = ctx.bundler_options.env_prefix;
|
||||
try bundle.runEnvLoader(false);
|
||||
const mini = bun.jsc.MiniEventLoop.initGlobal(bundle.env);
|
||||
var buf: bun.PathBuffer = undefined;
|
||||
|
||||
@@ -591,8 +591,8 @@ pub const RunCommand = struct {
|
||||
const args = ctx.args;
|
||||
|
||||
var this_transpiler = try transpiler.Transpiler.init(ctx.allocator, ctx.log, args, null);
|
||||
this_transpiler.options.env.behavior = api.DotEnvBehavior.load_all;
|
||||
this_transpiler.options.env.prefix = "";
|
||||
this_transpiler.options.env.behavior = ctx.bundler_options.env_behavior;
|
||||
this_transpiler.options.env.prefix = ctx.bundler_options.env_prefix;
|
||||
|
||||
this_transpiler.resolver.care_about_bin_folder = true;
|
||||
this_transpiler.resolver.care_about_scripts = true;
|
||||
@@ -777,9 +777,9 @@ pub const RunCommand = struct {
|
||||
) !*DirInfo {
|
||||
const args = ctx.args;
|
||||
this_transpiler.* = try transpiler.Transpiler.init(ctx.allocator, ctx.log, args, env);
|
||||
this_transpiler.options.env.behavior = api.DotEnvBehavior.load_all;
|
||||
this_transpiler.options.env.behavior = ctx.bundler_options.env_behavior;
|
||||
this_transpiler.env.quiet = true;
|
||||
this_transpiler.options.env.prefix = "";
|
||||
this_transpiler.options.env.prefix = ctx.bundler_options.env_prefix;
|
||||
|
||||
this_transpiler.resolver.care_about_bin_folder = true;
|
||||
this_transpiler.resolver.care_about_scripts = true;
|
||||
|
||||
@@ -315,7 +315,11 @@ class Query<T, Handle extends BaseQueryHandle<any>> extends PublicPromise<T> {
|
||||
|
||||
this.#runAsyncAndCatch();
|
||||
|
||||
return super.finally.$apply(this, arguments);
|
||||
if (arguments.length === 0) {
|
||||
return super.finally();
|
||||
}
|
||||
|
||||
return super.finally(_onfinally);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
248
test/cli/run/no-env.test.ts
Normal file
248
test/cli/run/no-env.test.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
|
||||
describe("--no-env-file flag", () => {
|
||||
test("should not load .env files when --no-env-file is specified", async () => {
|
||||
using dir = tempDir("test-no-env", {
|
||||
".env": "TEST_VAR=from_env_file",
|
||||
".env.local": "LOCAL_VAR=from_local_file",
|
||||
".env.development": "DEV_VAR=from_dev_file",
|
||||
"index.js": `
|
||||
console.log(JSON.stringify({
|
||||
TEST_VAR: process.env.TEST_VAR || 'undefined',
|
||||
LOCAL_VAR: process.env.LOCAL_VAR || 'undefined',
|
||||
DEV_VAR: process.env.DEV_VAR || 'undefined',
|
||||
PROCESS_VAR: process.env.PROCESS_VAR || 'undefined'
|
||||
}));
|
||||
`,
|
||||
});
|
||||
|
||||
// Test without --no-env-file (should load .env files)
|
||||
await using proc1 = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: { ...bunEnv, PROCESS_VAR: "from_process" },
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout1, stderr1, exitCode1] = await Promise.all([proc1.stdout.text(), proc1.stderr.text(), proc1.exited]);
|
||||
|
||||
expect(exitCode1).toBe(0);
|
||||
const result1 = JSON.parse(stdout1);
|
||||
expect(result1.TEST_VAR).toBe("from_env_file");
|
||||
expect(result1.LOCAL_VAR).toBe("from_local_file");
|
||||
expect(result1.DEV_VAR).toBe("from_dev_file");
|
||||
expect(result1.PROCESS_VAR).toBe("from_process");
|
||||
|
||||
// Test with --no-env-file (should NOT load .env files)
|
||||
await using proc2 = Bun.spawn({
|
||||
cmd: [bunExe(), "--no-env-file", "index.js"],
|
||||
env: { ...bunEnv, PROCESS_VAR: "from_process" },
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout2, stderr2, exitCode2] = await Promise.all([proc2.stdout.text(), proc2.stderr.text(), proc2.exited]);
|
||||
|
||||
expect(exitCode2).toBe(0);
|
||||
const result2 = JSON.parse(stdout2);
|
||||
expect(result2.TEST_VAR).toBe("undefined");
|
||||
expect(result2.LOCAL_VAR).toBe("undefined");
|
||||
expect(result2.DEV_VAR).toBe("undefined");
|
||||
expect(result2.PROCESS_VAR).toBe("from_process"); // Process env should still work
|
||||
});
|
||||
|
||||
test("--no-env-file should override --env-file", async () => {
|
||||
using dir = tempDir("test-no-env-file-override", {
|
||||
".env.custom": "CUSTOM_VAR=from_custom_file",
|
||||
"index.js": `
|
||||
console.log(process.env.CUSTOM_VAR || 'undefined');
|
||||
`,
|
||||
});
|
||||
|
||||
// Test with both --env-file and --no-env-file
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--env-file=.env.custom", "--no-env-file", "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout.trim()).toBe("undefined");
|
||||
});
|
||||
|
||||
test.todo("--no-env-file with bun test", async () => {
|
||||
using dir = tempDir("test-no-env-file-test", {
|
||||
".env": "TEST_VAR=from_env_file",
|
||||
"test.test.js": `
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
test("env test", () => {
|
||||
expect(process.env.TEST_VAR).toBeUndefined();
|
||||
expect(process.env.PROCESS_VAR).toBe("from_process");
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--no-env-file", "test", "test.test.js"],
|
||||
env: { ...bunEnv, PROCESS_VAR: "from_process" },
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toContain("1 pass");
|
||||
});
|
||||
|
||||
test("--no-env-file with bun run script", async () => {
|
||||
using dir = tempDir("test-no-env-file-run", {
|
||||
".env": "TEST_VAR=from_env_file",
|
||||
"package.json": JSON.stringify({
|
||||
scripts: {
|
||||
test: "node -e \"console.log(process.env.TEST_VAR || 'undefined')\"",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--no-env-file", "run", "test"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout.trim()).toBe("undefined");
|
||||
});
|
||||
});
|
||||
|
||||
describe("env.file = false in bunfig.toml", () => {
|
||||
test("should not load .env files when env.file = false", async () => {
|
||||
using dir = tempDir("test-bunfig-env-false", {
|
||||
".env": "TEST_VAR=from_env_file",
|
||||
".env.local": "LOCAL_VAR=from_local_file",
|
||||
"bunfig.toml": `
|
||||
[env]
|
||||
file = false
|
||||
`,
|
||||
"index.js": `
|
||||
console.log(JSON.stringify({
|
||||
TEST_VAR: process.env.TEST_VAR || 'undefined',
|
||||
LOCAL_VAR: process.env.LOCAL_VAR || 'undefined',
|
||||
PROCESS_VAR: process.env.PROCESS_VAR || 'undefined'
|
||||
}));
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: { ...bunEnv, PROCESS_VAR: "from_process" },
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const result = JSON.parse(stdout);
|
||||
expect(result.TEST_VAR).toBe("undefined");
|
||||
expect(result.LOCAL_VAR).toBe("undefined");
|
||||
expect(result.PROCESS_VAR).toBe("from_process");
|
||||
});
|
||||
|
||||
test("--env-file should override env.file = false", async () => {
|
||||
using dir = tempDir("test-bunfig-override", {
|
||||
".env.custom": "CUSTOM_VAR=from_custom_file",
|
||||
"bunfig.toml": `
|
||||
[env]
|
||||
file = false
|
||||
`,
|
||||
"index.js": `
|
||||
console.log(process.env.CUSTOM_VAR || 'undefined');
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--env-file=.env.custom", "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout.trim()).toBe("from_custom_file");
|
||||
});
|
||||
|
||||
test("env.file = false should disable env loading", async () => {
|
||||
using dir = tempDir("test-bunfig-env-false-2", {
|
||||
".env": "TEST_VAR=from_env_file",
|
||||
"bunfig.toml": `
|
||||
[env]
|
||||
file = false
|
||||
`,
|
||||
"index.js": `
|
||||
console.log(process.env.TEST_VAR || 'undefined');
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout.trim()).toBe("undefined");
|
||||
});
|
||||
|
||||
test.todo("bunfig with test command", async () => {
|
||||
using dir = tempDir("test-bunfig-test", {
|
||||
".env.test": "TEST_VAR=from_test_env",
|
||||
"bunfig.toml": `
|
||||
[env]
|
||||
file = false
|
||||
`,
|
||||
"test.test.js": `
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
test("env test", () => {
|
||||
expect(process.env.TEST_VAR).toBeUndefined();
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "test.test.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toContain("1 pass");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user