From af2317deb465082f837dbf1e74fbef568b5aa2c9 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Wed, 14 Jan 2026 07:13:17 +0100 Subject: [PATCH] fix(bundler): allow `reactFastRefresh` Bun.build option with non-browser targets (#26035) ### What does this PR do? Previously, reactFastRefresh was silently ignored when target was not 'browser', even when explicitly enabled. This was confusing as there was no warning or error. This change removes the `target == .browser` check, trusting explicit user intent. If users enable reactFastRefresh with a non-browser target, the transform will now be applied. If `$RefreshReg$` is not defined at runtime, it will fail fast with a clear error rather than silently doing nothing. Use case: Terminal UIs (like [termcast](https://termcast.app)) need React Fast Refresh with target: 'bun' for hot reloading in non-browser environments. ### How did you verify your code works? Updated existing test removing target browser --- src/bundler/ParseTask.zig | 3 +-- test/regression/issue/25716.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/bundler/ParseTask.zig b/src/bundler/ParseTask.zig index e8bbbcf696..0240f9e03e 100644 --- a/src/bundler/ParseTask.zig +++ b/src/bundler/ParseTask.zig @@ -1189,8 +1189,7 @@ fn runWithSourceCode( opts.features.bundler_feature_flags = transpiler.options.bundler_feature_flags; opts.features.hot_module_reloading = output_format == .internal_bake_dev and !source.index.isRuntime(); opts.features.auto_polyfill_require = output_format == .esm and !opts.features.hot_module_reloading; - opts.features.react_fast_refresh = target == .browser and - transpiler.options.react_fast_refresh and + opts.features.react_fast_refresh = transpiler.options.react_fast_refresh and loader.isJSX() and !source.path.isNodeModule(); diff --git a/test/regression/issue/25716.test.ts b/test/regression/issue/25716.test.ts index a21331f268..d5c13e421a 100644 --- a/test/regression/issue/25716.test.ts +++ b/test/regression/issue/25716.test.ts @@ -4,7 +4,7 @@ import { expect, test } from "bun:test"; import { tempDirWithFiles } from "harness"; import { join } from "path"; -test("Bun.build reactFastRefresh option enables React Fast Refresh transform", async () => { +test.each(["browser", "bun"] as const)("Bun.build reactFastRefresh works with target: %s", async (target) => { const dir = tempDirWithFiles("react-fast-refresh-test", { "component.tsx": ` import { useState } from "react"; @@ -24,7 +24,7 @@ test("Bun.build reactFastRefresh option enables React Fast Refresh transform", a const buildEnabled = await Bun.build({ entrypoints: [join(dir, "component.tsx")], reactFastRefresh: true, - target: "browser", + target, external: ["react"], }); @@ -38,7 +38,7 @@ test("Bun.build reactFastRefresh option enables React Fast Refresh transform", a // Without reactFastRefresh (default), output should NOT contain refresh calls const buildDisabled = await Bun.build({ entrypoints: [join(dir, "component.tsx")], - target: "browser", + target, external: ["react"], });