Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
71fccac84c fix(bundler): honor env: 'disable' for NODE_ENV and BUN_ENV
When `env: 'disable'` was set in Bun.build options, process.env.NODE_ENV
and process.env.BUN_ENV were still being inlined as compile-time defines.
The condition in definesFromTransformOptions only excluded
.load_all_without_inlining but not .disable, causing NODE_ENV to be
replaced with "development" and comparisons to be constant-folded.

Closes #22820

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-19 09:23:21 +00:00
3 changed files with 74 additions and 1 deletions

View File

@@ -1483,7 +1483,7 @@ pub fn definesFromTransformOptions(
);
}
if (behavior != .load_all_without_inlining) {
if (behavior != .load_all_without_inlining and behavior != .disable) {
const quoted_node_env: string = brk: {
if (NODE_ENV) |node_env| {
if (node_env.len > 0) {

View File

@@ -65,6 +65,24 @@ for (let backend of ["api", "cli"] as const) {
},
});
// Test disable mode - NODE_ENV is also not inlined (regression #22820)
itBundled("env/disable-node-env", {
env: {
NODE_ENV: "production",
},
backend: backend,
dotenv: "disable",
files: {
"/a.js": `
console.log(process.env.NODE_ENV);
console.log(process.env.NODE_ENV !== "production");
`,
},
run: {
stdout: "undefined\ntrue\n",
},
});
// TODO: make this work as expected with process.env isntead of relying on the initial env vars.
// Test pattern matching - only vars with prefix are inlined
if (backend === "cli")

View File

@@ -0,0 +1,55 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
// https://github.com/oven-sh/bun/issues/22820
// Bun.build with env: 'disable' should not inline process.env.NODE_ENV
test("Bun.build env: 'disable' does not inline NODE_ENV", async () => {
using dir = tempDir("issue-22820", {
"program.ts": `
console.log(process.env.NODE_ENV);
console.log(process.env.SOMETHING_ELSE);
console.log(process.env.NODE_ENV !== "production");
console.log(process.env.SOMETHING_ELSE !== "production");
`,
"build.ts": `
const result = await Bun.build({
entrypoints: ['./program.ts'],
outdir: './dist',
target: 'bun',
env: 'disable',
});
if (!result.success) {
console.error(result.logs);
process.exit(1);
}
`,
});
// Run the build
await using buildProc = Bun.spawn({
cmd: [bunExe(), "build.ts"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [buildStdout, buildStderr, buildExit] = await Promise.all([
buildProc.stdout.text(),
buildProc.stderr.text(),
buildProc.exited,
]);
expect(buildStderr).toBe("");
expect(buildExit).toBe(0);
// Read the bundled output and verify NODE_ENV is NOT inlined
const output = await Bun.file(`${dir}/dist/program.js`).text();
// process.env.NODE_ENV should appear twice (not replaced with a string literal)
expect(output.match(/process\.env\.NODE_ENV/g)?.length).toBe(2);
expect(output).toContain("process.env.SOMETHING_ELSE");
// "development" should NOT appear — that was the buggy inlined value
expect(output).not.toContain('"development"');
// The comparison should NOT be constant-folded to `true`
expect(output).not.toMatch(/console\.log\(true\)/);
});