Add compile-time flags to control .env and bunfig.toml autoloading (#24790)

## Summary

This PR adds two new compile options to control whether standalone
executables autoload `.env` files and `bunfig.toml` configuration files.

## New Options

### JavaScript API
```js
await Bun.build({
  entrypoints: ["./entry.ts"],
  compile: {
    autoloadDotenv: false,  // Disable .env loading (default: true)
    autoloadBunfig: false,  // Disable bunfig.toml loading (default: true)
  }
});
```

### CLI Flags
```bash
bun build --compile --no-compile-autoload-dotenv entry.ts
bun build --compile --no-compile-autoload-bunfig entry.ts
bun build --compile --compile-autoload-dotenv entry.ts
bun build --compile --compile-autoload-bunfig entry.ts
```

## Implementation

The flags are stored in a new `Flags` packed struct in
`StandaloneModuleGraph`:
```zig
pub const Flags = packed struct(u32) {
    disable_default_env_files: bool = false,
    disable_autoload_bunfig: bool = false,
    _padding: u30 = 0,
};
```

These flags are:
1. Set during compilation from CLI args or JS API options
2. Serialized into the `StandaloneModuleGraph` embedded in the
executable
3. Read at runtime in `bootStandalone()` to conditionally load config
files

## Testing

Manually tested and verified:
-  Default behavior loads `.env` files
-  `--no-compile-autoload-dotenv` disables `.env` loading
-  `--compile-autoload-dotenv` explicitly enables `.env` loading
-  Default behavior loads `bunfig.toml` (verified with preload script)
-  `--no-compile-autoload-bunfig` disables `bunfig.toml` loading

Test cases added in `test/bundler/bundler_compile_autoload.test.ts`

## Files Changed

- `src/StandaloneModuleGraph.zig` - Added Flags struct, updated
encode/decode
- `src/bun.js.zig` - Checks flags in bootStandalone()
- `src/bun.js/api/JSBundler.zig` - Added autoload options to
CompileOptions
- `src/bundler/bundle_v2.zig` - Pass flags to toExecutable()
- `src/cli.zig` - Added flags to BundlerOptions
- `src/cli/Arguments.zig` - Added CLI argument parsing
- `src/cli/build_command.zig` - Pass flags from context
- `test/bundler/expectBundled.ts` - Support new compile options
- `test/bundler/bundler_compile_autoload.test.ts` - New test file

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
This commit is contained in:
robobun
2025-11-18 06:46:44 -08:00
committed by GitHub
parent 9513c1d1d9
commit 7c485177ee
10 changed files with 387 additions and 4 deletions

View File

@@ -0,0 +1,267 @@
import { describe } from "bun:test";
import { itBundled } from "./expectBundled";
describe("bundler", () => {
// Test that .env files are loaded by default in standalone executables
itBundled("compile/AutoloadDotenvDefault", {
compile: true,
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
},
run: {
stdout: "from_dotenv",
setCwd: true,
},
});
// Test that .env files can be disabled with autoloadDotenv: false
itBundled("compile/AutoloadDotenvDisabled", {
compile: {
autoloadDotenv: false,
},
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
},
run: {
stdout: "not found",
setCwd: true,
},
});
// Test that .env files can be explicitly enabled with autoloadDotenv: true
itBundled("compile/AutoloadDotenvEnabledExplicitly", {
compile: {
autoloadDotenv: true,
},
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
},
run: {
stdout: "from_dotenv",
setCwd: true,
},
});
// Test that process environment variables take precedence over .env files
itBundled("compile/AutoloadDotenvWithExistingEnv", {
compile: true,
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
},
run: {
stdout: "from_shell",
setCwd: true,
env: {
TEST_VAR: "from_shell",
},
},
});
// Test that bunfig.toml is loaded by default (preload is executed)
itBundled("compile/AutoloadBunfigDefault", {
compile: true,
files: {
"/entry.ts": /* js */ `
console.log("ENTRY");
`,
},
runtimeFiles: {
"/bunfig.toml": `
preload = ["./preload.ts"]
`,
"/preload.ts": `
console.log("PRELOAD");
`,
},
run: {
stdout: "PRELOAD\nENTRY",
setCwd: true,
},
});
// Test that bunfig.toml can be disabled with autoloadBunfig: false
itBundled("compile/AutoloadBunfigDisabled", {
compile: {
autoloadBunfig: false,
},
files: {
"/entry.ts": /* js */ `
console.log("ENTRY");
`,
},
runtimeFiles: {
"/bunfig.toml": `
preload = ["./preload.ts"]
`,
"/preload.ts": `
console.log("PRELOAD");
`,
},
run: {
// When bunfig is disabled, preload should NOT execute
stdout: "ENTRY",
setCwd: true,
},
});
// Test that bunfig.toml can be explicitly enabled with autoloadBunfig: true
itBundled("compile/AutoloadBunfigEnabled", {
compile: {
autoloadBunfig: true,
},
files: {
"/entry.ts": /* js */ `
console.log("ENTRY");
`,
},
runtimeFiles: {
"/bunfig.toml": `
preload = ["./preload.ts"]
`,
"/preload.ts": `
console.log("PRELOAD");
`,
},
run: {
stdout: "PRELOAD\nENTRY",
setCwd: true,
},
});
// Test CLI backend with autoloadDotenv: false
itBundled("compile/AutoloadDotenvDisabledCLI", {
compile: {
autoloadDotenv: false,
},
backend: "cli",
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
},
run: {
stdout: "not found",
setCwd: true,
},
});
// Test CLI backend with autoloadDotenv: true
itBundled("compile/AutoloadDotenvEnabledCLI", {
compile: {
autoloadDotenv: true,
},
backend: "cli",
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
},
run: {
stdout: "from_dotenv",
setCwd: true,
},
});
// Test CLI backend with autoloadBunfig: false
itBundled("compile/AutoloadBunfigDisabledCLI", {
compile: {
autoloadBunfig: false,
},
backend: "cli",
files: {
"/entry.ts": /* js */ `
console.log("ENTRY");
`,
},
runtimeFiles: {
"/bunfig.toml": `
preload = ["./preload.ts"]
`,
"/preload.ts": `
console.log("PRELOAD");
`,
},
run: {
stdout: "ENTRY",
setCwd: true,
},
});
// Test CLI backend with autoloadBunfig: true
itBundled("compile/AutoloadBunfigEnabledCLI", {
compile: {
autoloadBunfig: true,
},
backend: "cli",
files: {
"/entry.ts": /* js */ `
console.log("ENTRY");
`,
},
runtimeFiles: {
"/bunfig.toml": `
preload = ["./preload.ts"]
`,
"/preload.ts": `
console.log("PRELOAD");
`,
},
run: {
stdout: "PRELOAD\nENTRY",
setCwd: true,
},
});
// Test that both flags can be disabled together without interference
itBundled("compile/AutoloadBothDisabled", {
compile: {
autoloadDotenv: false,
autoloadBunfig: false,
},
files: {
"/entry.ts": /* js */ `
console.log(process.env.TEST_VAR || "not found");
console.log("ENTRY");
`,
},
runtimeFiles: {
"/.env": `TEST_VAR=from_dotenv`,
"/bunfig.toml": `
preload = ["./preload.ts"]
`,
"/preload.ts": `
console.log("PRELOAD");
`,
},
run: {
stdout: "not found\nENTRY",
setCwd: true,
},
});
});

View File

@@ -702,6 +702,16 @@ function expectBundled(
? Object.entries(bundleErrors).flatMap(([file, v]) => v.map(error => ({ file, error })))
: null;
// Helper to add compile boolean flags
const compileFlag = (prop: string, trueFlag: string, falseFlag: string): string[] => {
if (compile && typeof compile === "object" && Object.prototype.hasOwnProperty.call(compile, prop)) {
const value = (compile as any)[prop];
if (value === true) return [trueFlag];
if (value === false) return [falseFlag];
}
return [];
};
if (backend === "cli") {
if (plugins) {
throw new Error("plugins not possible in backend=CLI");
@@ -719,6 +729,8 @@ function expectBundled(
compile && typeof compile === "object" && "execArgv" in compile
? `--compile-exec-argv=${Array.isArray(compile.execArgv) ? compile.execArgv.join(" ") : compile.execArgv}`
: [],
compileFlag("autoloadDotenv", "--compile-autoload-dotenv", "--no-compile-autoload-dotenv"),
compileFlag("autoloadBunfig", "--compile-autoload-bunfig", "--no-compile-autoload-bunfig"),
outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]),
`--target=${target}`,
@@ -1058,6 +1070,12 @@ function expectBundled(
target: compile,
outfile: outfile,
};
} else if (typeof compile === "object") {
// When compile is already an object, ensure it has outfile set
compile = {
...compile,
outfile: outfile,
};
}
}