mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
feat(bundler): expose reactFastRefresh option in Bun.build API (#25731)
Fixes #25716 Adds support for a `reactFastRefresh: boolean` option in the `Bun.build` JavaScript API, matching the existing `--react-fast-refresh` CLI flag. ```ts const result = await Bun.build({ reactFastRefresh: true, entrypoints: ["src/App.tsx"], }); ``` When enabled, the bundler adds React Fast Refresh transform code (`$RefreshReg$`, `$RefreshSig$`) to the output.
This commit is contained in:
10
packages/bun-types/bun.d.ts
vendored
10
packages/bun-types/bun.d.ts
vendored
@@ -1942,6 +1942,16 @@ declare module "bun" {
|
||||
development?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable React Fast Refresh transform.
|
||||
*
|
||||
* This adds the necessary code transformations for React Fast Refresh (hot module
|
||||
* replacement for React components), but does not emit hot-module code itself.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
reactFastRefresh?: boolean;
|
||||
|
||||
outdir?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ pub const JSBundler = struct {
|
||||
target: Target = Target.browser,
|
||||
entry_points: bun.StringSet = bun.StringSet.init(bun.default_allocator),
|
||||
hot: bool = false,
|
||||
react_fast_refresh: bool = false,
|
||||
define: bun.StringMap = bun.StringMap.init(bun.default_allocator, false),
|
||||
loaders: ?api.LoaderMap = null,
|
||||
dir: OwnedString = OwnedString.initEmpty(bun.default_allocator),
|
||||
@@ -341,6 +342,10 @@ pub const JSBundler = struct {
|
||||
}
|
||||
}
|
||||
|
||||
if (try config.getBooleanLoose(globalThis, "reactFastRefresh")) |react_fast_refresh| {
|
||||
this.react_fast_refresh = react_fast_refresh;
|
||||
}
|
||||
|
||||
var has_out_dir = false;
|
||||
if (try config.getOptional(globalThis, "outdir", ZigString.Slice)) |slice| {
|
||||
defer slice.deinit();
|
||||
|
||||
@@ -1904,6 +1904,7 @@ pub const BundleV2 = struct {
|
||||
transpiler.options.css_chunking = config.css_chunking;
|
||||
transpiler.options.banner = config.banner.slice();
|
||||
transpiler.options.footer = config.footer.slice();
|
||||
transpiler.options.react_fast_refresh = config.react_fast_refresh;
|
||||
|
||||
if (transpiler.options.compile) {
|
||||
// Emitting DCE annotations is nonsensical in --compile.
|
||||
|
||||
49
test/regression/issue/25716.test.ts
Normal file
49
test/regression/issue/25716.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
// https://github.com/oven-sh/bun/issues/25716
|
||||
// Expose `--react-fast-refresh` option in `Bun.build` JS API
|
||||
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 () => {
|
||||
const dir = tempDirWithFiles("react-fast-refresh-test", {
|
||||
"component.tsx": `
|
||||
import { useState } from "react";
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <div><Counter /></div>;
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
// With reactFastRefresh: true, output should contain $RefreshReg$ and $RefreshSig$
|
||||
const buildEnabled = await Bun.build({
|
||||
entrypoints: [join(dir, "component.tsx")],
|
||||
reactFastRefresh: true,
|
||||
target: "browser",
|
||||
external: ["react"],
|
||||
});
|
||||
|
||||
expect(buildEnabled.success).toBe(true);
|
||||
expect(buildEnabled.outputs).toHaveLength(1);
|
||||
|
||||
const outputEnabled = await buildEnabled.outputs[0].text();
|
||||
expect(outputEnabled).toContain("$RefreshReg$");
|
||||
expect(outputEnabled).toContain("$RefreshSig$");
|
||||
|
||||
// Without reactFastRefresh (default), output should NOT contain refresh calls
|
||||
const buildDisabled = await Bun.build({
|
||||
entrypoints: [join(dir, "component.tsx")],
|
||||
target: "browser",
|
||||
external: ["react"],
|
||||
});
|
||||
|
||||
expect(buildDisabled.success).toBe(true);
|
||||
const outputDisabled = await buildDisabled.outputs[0].text();
|
||||
expect(outputDisabled).not.toContain("$RefreshReg$");
|
||||
expect(outputDisabled).not.toContain("$RefreshSig$");
|
||||
});
|
||||
Reference in New Issue
Block a user