feat(FN-5611): strip react devtools from bun compile builds
Complete the standalone build pipeline fix by setting `DEV=false` during bun compile to eliminate React DevTools from production executables, with hardened regression tests and documentation. Fusion-Task-Id: FN-5611 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> Fusion-Task-Id: FN-5611
This commit is contained in:
5
.changeset/fn-5611-bun-compile-react-devtools.md
Normal file
5
.changeset/fn-5611-bun-compile-react-devtools.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix the Bun-compiled `fn` executable so `--help` no longer crashes with a missing `react-devtools-core` module. The build now defines `process.env.DEV` as `false` during compile, allowing Ink's DEV-only devtools import path to be removed from the bundled binary.
|
||||
@@ -160,6 +160,8 @@ Prebuilt standalone binaries are available that require no Node.js runtime. You
|
||||
bun run build.ts
|
||||
```
|
||||
|
||||
The standalone build defines `process.env.DEV` as `false` at compile time so Ink's DEV-only React devtools import path is removed from the binary (preventing runtime `react-devtools-core` resolution failures).
|
||||
|
||||
### Runtime Assets
|
||||
|
||||
When using standalone binaries, the dashboard's integrated terminal requires native platform assets that must be co-located with the binary:
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
* Notes:
|
||||
* - If dashboard client assets are missing, this script generates a
|
||||
* minimal dist/client/index.html stub so clean-checkout tests can run.
|
||||
* - Ink's DEV-only react-devtools import is eliminated at compile time via
|
||||
* --define "process.env.DEV='false'" to keep the standalone binary
|
||||
* self-contained without node_modules.
|
||||
*/
|
||||
|
||||
import { join, dirname } from "node:path";
|
||||
@@ -339,10 +342,11 @@ function compileBinary(outFile: string, target: string, isCrossCompile: boolean)
|
||||
target,
|
||||
"--minify",
|
||||
"--conditions=source",
|
||||
// ink imports react-devtools-core dynamically only when DEV=true; mark
|
||||
// external so Bun's static bundler doesn't try to resolve it at compile.
|
||||
"--external",
|
||||
"react-devtools-core",
|
||||
// Ink conditionally loads devtools when process.env.DEV === "true".
|
||||
// Force DEV to false at compile-time so Bun/minify can eliminate that branch
|
||||
// and avoid shipping runtime references to react-devtools-core.
|
||||
"--define",
|
||||
"process.env.DEV='false'",
|
||||
// cpu-features: native .node binding from ssh2 (transitive via dockerode); ssh2 falls back to pure JS when unavailable
|
||||
"--external",
|
||||
"cpu-features",
|
||||
|
||||
@@ -90,7 +90,14 @@ describe.skipIf(!SHOULD_RUN_BUILD_EXE)("build-exe-cross: --all builds all platfo
|
||||
const allBuilt = SUPPORTED_TARGETS.every((target) =>
|
||||
existsSync(join(distDir, expectedBinaryName(target))),
|
||||
);
|
||||
if (allBuilt) return;
|
||||
const platform = process.platform === "darwin" ? "darwin" :
|
||||
process.platform === "linux" ? "linux" :
|
||||
process.platform === "win32" ? "win32" : "unknown";
|
||||
const arch = process.arch === "arm64" ? "arm64" :
|
||||
process.arch === "x64" ? "x64" : "unknown";
|
||||
const hostRuntimeDir = join(distDir, "runtime", `${platform}-${arch}`);
|
||||
const hostRuntimeReady = existsSync(join(hostRuntimeDir, "pty.node"));
|
||||
if (allBuilt && hostRuntimeReady) return;
|
||||
execSync("bun run build.ts --all", {
|
||||
cwd: cliRoot,
|
||||
stdio: "pipe",
|
||||
@@ -121,6 +128,16 @@ describe.skipIf(!SHOULD_RUN_BUILD_EXE)("build-exe-cross: --all builds all platfo
|
||||
const prebuildName = `${platform}-${arch}`;
|
||||
const runtimeDir = join(distDir, "runtime", prebuildName);
|
||||
|
||||
if (!existsSync(join(runtimeDir, "pty.node"))) {
|
||||
// Ensure host-native runtime assets exist even if a previous --all build
|
||||
// ended on a non-host target.
|
||||
execSync("bun run build.ts", {
|
||||
cwd: cliRoot,
|
||||
stdio: "pipe",
|
||||
timeout: 120_000,
|
||||
});
|
||||
}
|
||||
|
||||
// pty.node is required for all platforms
|
||||
expect(existsSync(join(runtimeDir, "pty.node"))).toBe(true);
|
||||
// spawn-helper is only for Unix platforms
|
||||
|
||||
@@ -10,6 +10,7 @@ const outBinary = join(cliRoot, "dist", process.platform === "win32" ? "fn.exe"
|
||||
const binaryName = process.platform === "win32" ? "fn.exe" : "fn";
|
||||
const clientDir = join(cliRoot, "dist", "client");
|
||||
const runtimeDir = join(cliRoot, "dist", "runtime");
|
||||
const childEnv = { ...process.env, VITEST: "false" };
|
||||
|
||||
/**
|
||||
* Create an isolated temp directory containing the binary, client/,
|
||||
@@ -72,6 +73,7 @@ type AsyncSpawnResult = {
|
||||
async function runCommandWithTimeout(binary: string, args: string[], timeoutMs: number): Promise<AsyncSpawnResult> {
|
||||
const child = spawn(binary, args, {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: childEnv,
|
||||
});
|
||||
|
||||
let stdout = "";
|
||||
@@ -179,6 +181,7 @@ describe("build-exe", () => {
|
||||
expect(result.stdout).toContain("dashboard");
|
||||
expect(result.stdout).toContain("task create");
|
||||
expect(result.stdout).toContain("task list");
|
||||
expect(result.stderr).not.toContain("react-devtools-core");
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
@@ -192,12 +195,14 @@ describe("build-exe", () => {
|
||||
const result = spawnSync(binary, ["task", "list"], {
|
||||
encoding: "utf-8",
|
||||
timeout: 30_000,
|
||||
env: childEnv,
|
||||
});
|
||||
if (hasKnownBunSqliteLimitation(result)) {
|
||||
return;
|
||||
}
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toContain("No tasks yet");
|
||||
expect(result.stderr ?? "").not.toContain("react-devtools-core");
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
@@ -212,9 +217,10 @@ describe("build-exe", () => {
|
||||
try {
|
||||
// Ask the OS for an ephemeral port so this smoke test never collides
|
||||
// with a developer's running dashboard or dev server.
|
||||
child = spawn(binary, ["dashboard", "-p", "0"], {
|
||||
child = spawn(binary, ["dashboard", "-p", "0", "--no-auth"], {
|
||||
cwd: dir,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: childEnv,
|
||||
});
|
||||
|
||||
if (!child.stdout || !child.stderr) {
|
||||
|
||||
Reference in New Issue
Block a user