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"],