diff --git a/src/bundler/transpiler.rs b/src/bundler/transpiler.rs
index cd24d1c280bc..0217b875dcbf 100644
--- a/src/bundler/transpiler.rs
+++ b/src/bundler/transpiler.rs
@@ -542,6 +542,7 @@ impl<'a> Transpiler<'a> {
let env_loader = self.env_mut();
let mut is_production = env_loader.is_production();
+ let mut is_development = env_loader.get_node_env() == Some(b"development");
// `load_defines` injects a default `process.env.NODE_ENV`; sample the
// explicit sources first so that default isn't mistaken for user intent
@@ -572,15 +573,16 @@ impl<'a> Transpiler<'a> {
// inside the `&mut self` scope without `unsafe`.
self.options.load_defines(self.arena, Some(env_loader))?;
- let mut is_development = false;
if had_explicit_node_env {
if let Some(node_env) = self.options.define.dots.get(b"NODE_ENV".as_slice()) {
if !node_env.is_empty() {
if let Some(s) = node_env[0].data.value.e_string() {
if s.eql_comptime(b"production") {
is_production = true;
+ is_development = false;
} else if s.eql_comptime(b"development") {
is_development = true;
+ is_production = false;
}
}
}
diff --git a/test/bundler/transpiler/jsx-tsconfig-react-jsx.test.ts b/test/bundler/transpiler/jsx-tsconfig-react-jsx.test.ts
index 0b46adc2be8e..334083e59881 100644
--- a/test/bundler/transpiler/jsx-tsconfig-react-jsx.test.ts
+++ b/test/bundler/transpiler/jsx-tsconfig-react-jsx.test.ts
@@ -28,6 +28,57 @@ const shimFiles = {
"m.jsx": `const a =
x
;\nglobalThis.s = a;\n`,
};
+// `NODE_ENV` in the environment overrides tsconfig dev/prod for `bun run` in
+// both directions; `--define process.env.NODE_ENV` overrides both.
+describe.each(["NODE_ENV", "BUN_ENV"])("bun run: env %s overrides tsconfig jsx dev/prod", envVar => {
+ test.concurrent.each([
+ // [tsconfig jsx, env value, expected runtime]
+ ["react-jsx", "development", "dev jsxDEV"], // env wins
+ ["react-jsx", "production", "prod jsx"], // both agree
+ ["react-jsxdev", "development", "dev jsxDEV"], // both agree
+ ["react-jsxdev", "production", "prod jsx"], // env wins
+ ] as const)(`tsconfig "%s" + ${envVar}=%s -> %s`, async (jsx, envValue, expected) => {
+ using dir = tempDir("jsx-tsconfig-env", {
+ ...shimFiles,
+ "tsconfig.json": JSON.stringify({ compilerOptions: { jsx, jsxImportSource: "shim" } }),
+ });
+ await using proc = Bun.spawn({
+ cmd: [bunExe(), "run", "m.jsx"],
+ env: { ...bunEnv, NODE_ENV: undefined, BUN_ENV: undefined, [envVar]: envValue },
+ cwd: String(dir),
+ stdout: "pipe",
+ stderr: "pipe",
+ });
+ const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
+ expect({ stderr, stdout: stdout.trim(), exitCode }).toEqual({ stderr: "", stdout: expected, exitCode: 0 });
+ });
+});
+
+describe("bun run: --define process.env.NODE_ENV overrides env NODE_ENV for jsx dev/prod", () => {
+ test.concurrent.each([
+ // [env NODE_ENV, --define value, tsconfig jsx, expected]
+ ["development", '"production"', "react-jsxdev", "prod jsx"], // --define > env > tsconfig
+ ["production", '"development"', "react-jsx", "dev jsxDEV"], // --define > env > tsconfig
+ ] as const)(
+ "env NODE_ENV=%s + --define process.env.NODE_ENV=%s (tsconfig %s) -> %s",
+ async (envValue, defineValue, jsx, expected) => {
+ using dir = tempDir("jsx-tsconfig-define", {
+ ...shimFiles,
+ "tsconfig.json": JSON.stringify({ compilerOptions: { jsx, jsxImportSource: "shim" } }),
+ });
+ await using proc = Bun.spawn({
+ cmd: [bunExe(), "run", "--define", `process.env.NODE_ENV=${defineValue}`, "m.jsx"],
+ env: { ...bunEnv, NODE_ENV: envValue, BUN_ENV: undefined },
+ cwd: String(dir),
+ stdout: "pipe",
+ stderr: "pipe",
+ });
+ const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
+ expect({ stderr, stdout: stdout.trim(), exitCode }).toEqual({ stderr: "", stdout: expected, exitCode: 0 });
+ },
+ );
+});
+
describe("tsconfig compilerOptions.jsx", () => {
test.each([
["react-jsx", "prod jsx", "shim/jsx-runtime"],