Compare commits

...

2 Commits

Author SHA1 Message Date
autofix-ci[bot]
f051ae4d9c Fix Bun.build({ env: "disable" }) not disabling NODE_ENV inlining
The JavaScript API was not properly handling env: "disable" - it needed to track when env was explicitly disabled and use .load_all_without_inlining behavior to prevent NODE_ENV from being inlined.

Fixes #19508

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 02:02:28 +00:00
Claude Bot
c75a8d295e Fix Bun.build({ env: "disable" }) not disabling NODE_ENV inlining
The env: "disable" option was being parsed correctly in the JavaScript API but NODE_ENV was still being inlined due to a logic error in definesFromTransformOptions.

When behavior is .disable, NODE_ENV should not be added to user_defines at all.

Fixes #19508

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 13:39:38 +00:00
3 changed files with 144 additions and 0 deletions

View File

@@ -35,6 +35,7 @@ pub const JSBundler = struct {
has_any_on_before_parse: bool = false,
throw_on_error: bool = true,
env_behavior: api.DotEnvBehavior = .disable,
env_explicitly_disabled: bool = false,
env_prefix: OwnedString = OwnedString.initEmpty(bun.default_allocator),
tsconfig_override: OwnedString = OwnedString.initEmpty(bun.default_allocator),
compile: ?CompileOptions = null,
@@ -352,6 +353,7 @@ pub const JSBundler = struct {
if (!env.isUndefined()) {
if (env == .null or env == .false or (env.isNumber() and env.asNumber() == 0)) {
this.env_behavior = .disable;
this.env_explicitly_disabled = true;
} else if (env == .true or (env.isNumber() and env.asNumber() == 1)) {
this.env_behavior = .load_all;
} else if (env.isString()) {
@@ -361,6 +363,7 @@ pub const JSBundler = struct {
this.env_behavior = .load_all;
} else if (strings.eqlComptime(slice.slice(), "disable")) {
this.env_behavior = .disable;
this.env_explicitly_disabled = true;
} else if (strings.indexOfChar(slice.slice(), '*')) |asterisk| {
if (asterisk > 0) {
this.env_behavior = .prefix;

View File

@@ -1822,6 +1822,10 @@ pub const BundleV2 = struct {
);
transpiler.options.env.behavior = config.env_behavior;
transpiler.options.env.prefix = config.env_prefix.slice();
// If env was explicitly disabled, prevent NODE_ENV inlining
if (config.env_explicitly_disabled) {
transpiler.options.env.behavior = .load_all_without_inlining;
}
if (config.force_node_env != .unspecified) {
transpiler.options.force_node_env = config.force_node_env;
}

View File

@@ -0,0 +1,137 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
import { join } from "path";
test("Bun.build with env: 'disable' should not inline process.env.NODE_ENV (issue #19508)", async () => {
using dir = tempDir("build-env-disable", {
"input.js": `console.log(process.env.NODE_ENV);`,
"build.js": `
import { build } from "bun";
const result = await build({
entrypoints: ["./input.js"],
outdir: "./dist",
env: "disable",
});
if (!result.success) {
throw new Error("Build failed");
}
`,
});
// Run the build
const buildProc = Bun.spawn({
cmd: [bunExe(), "build.js"],
env: { ...bunEnv, NODE_ENV: "production" },
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [buildStdout, buildStderr, buildExitCode] = await Promise.all([
buildProc.stdout.text(),
buildProc.stderr.text(),
buildProc.exited,
]);
expect(buildExitCode).toBe(0);
// Read the output file
const outputFile = Bun.file(join(String(dir), "dist", "input.js"));
const outputContent = await outputFile.text();
// The output should contain process.env.NODE_ENV, not the inlined value
expect(outputContent).toContain("process.env.NODE_ENV");
expect(outputContent).not.toContain('"production"');
expect(outputContent).not.toContain('"development"');
// Also test that it works correctly at runtime
const runProc = Bun.spawn({
cmd: [bunExe(), join("dist", "input.js")],
env: { ...bunEnv, NODE_ENV: "test-runtime" },
cwd: String(dir),
stdout: "pipe",
});
const [runStdout, runExitCode] = await Promise.all([runProc.stdout.text(), runProc.exited]);
expect(runExitCode).toBe(0);
expect(runStdout.trim()).toBe("test-runtime");
});
test("Bun.build CLI with --env=disable should not inline process.env.NODE_ENV", async () => {
using dir = tempDir("build-cli-env-disable", {
"input.js": `console.log(process.env.NODE_ENV);`,
});
// Run the build via CLI
const buildProc = Bun.spawn({
cmd: [bunExe(), "build", "./input.js", "--outdir", "./dist", "--env=disable"],
env: { ...bunEnv, NODE_ENV: "production" },
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [buildStdout, buildStderr, buildExitCode] = await Promise.all([
buildProc.stdout.text(),
buildProc.stderr.text(),
buildProc.exited,
]);
expect(buildExitCode).toBe(0);
// Read the output file
const outputFile = Bun.file(join(String(dir), "dist", "input.js"));
const outputContent = await outputFile.text();
// The output should contain process.env.NODE_ENV, not the inlined value
expect(outputContent).toContain("process.env.NODE_ENV");
expect(outputContent).not.toContain('"production"');
expect(outputContent).not.toContain('"development"');
});
test("Bun.build with env: 'inline' should inline process.env.NODE_ENV", async () => {
using dir = tempDir("build-env-inline", {
"input.js": `console.log(process.env.NODE_ENV);`,
"build.js": `
import { build } from "bun";
const result = await build({
entrypoints: ["./input.js"],
outdir: "./dist",
env: "inline",
});
if (!result.success) {
throw new Error("Build failed");
}
`,
});
// Run the build with NODE_ENV=production
const buildProc = Bun.spawn({
cmd: [bunExe(), "build.js"],
env: { ...bunEnv, NODE_ENV: "production" },
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [buildStdout, buildStderr, buildExitCode] = await Promise.all([
buildProc.stdout.text(),
buildProc.stderr.text(),
buildProc.exited,
]);
expect(buildExitCode).toBe(0);
// Read the output file
const outputFile = Bun.file(join(String(dir), "dist", "input.js"));
const outputContent = await outputFile.text();
// The output should contain the inlined value, not process.env.NODE_ENV
expect(outputContent).not.toContain("process.env.NODE_ENV");
expect(outputContent).toContain('"production"');
});