Compare commits

...

3 Commits

Author SHA1 Message Date
Jarred Sumner
1be3561047 Merge branch 'main' into claude/fix-embed-8-files-crash 2025-12-16 19:53:22 -08:00
autofix-ci[bot]
602f95055c [autofix.ci] apply automated fixes 2025-12-11 15:52:24 +00:00
robobun
04ceae0381 fix: standalone executables with 8+ embedded files silently exiting
Fixes #20821

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-12 02:50:13 +11:00
3 changed files with 73 additions and 6 deletions

View File

@@ -386,7 +386,12 @@ pub const StandaloneModuleGraph = struct {
if (entry_point_id == null) {
if (output_file.side == null or output_file.side.? == .server) {
if (output_file.output_kind == .@"entry-point") {
entry_point_id = module_count;
// When there are multiple entry points passed to the bundler (e.g., `bun build --compile app.js assets/*`),
// we need to find the *main* entry point (entry_point_index == 0), not just any entry-point.
// If entry_point_index is not set, we fall back to the first server-side entry-point found.
if (output_file.entry_point_index == null or output_file.entry_point_index.? == 0) {
entry_point_id = module_count;
}
}
}
}

View File

@@ -347,12 +347,13 @@ pub const BuildCommand = struct {
if (output_dir.len == 0 and outfile.len > 0 and will_be_one_file) {
output_dir = std.fs.path.dirname(outfile) orelse ".";
if (ctx.bundler_options.compile) {
// If the first output file happens to be a client-side chunk imported server-side
// then don't rename it to something else, since an HTML
// import manifest might depend on the file path being the
// one we think it should be.
// Find the main entry point (entry_point_index == 0) to rename it to the outfile name.
// We must check entry_point_index because when multiple entry points are provided
// (e.g., `bun build --compile app.js assets/*`), the output files may be sorted
// in a different order and the first server-side entry-point might not be the
// main entry point that should become the executable.
for (output_files) |*f| {
if (f.output_kind == .@"entry-point" and (f.side orelse .server) == .server) {
if (f.output_kind == .@"entry-point" and (f.side orelse .server) == .server and f.entry_point_index == 0) {
f.dest_path = std.fs.path.basename(outfile);
break;
}

View File

@@ -0,0 +1,61 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
// https://github.com/oven-sh/bun/issues/20821
// Standalone executables with 8+ embedded files would silently exit without running
// because the entry point was incorrectly identified when multiple files were passed
// to `bun build --compile`.
test("compile with 8+ embedded files runs correctly", async () => {
using dir = tempDir("issue-20821", {
"app.js": `console.log("IT WORKS", Bun.embeddedFiles.length);`,
"assets/file-1": "",
"assets/file-2": "",
"assets/file-3": "",
"assets/file-4": "",
"assets/file-5": "",
"assets/file-6": "",
"assets/file-7": "",
"assets/file-8": "",
});
// Build the executable with 8 embedded files
await using buildProc = Bun.spawn({
cmd: [
bunExe(),
"build",
"--compile",
"app.js",
"assets/file-1",
"assets/file-2",
"assets/file-3",
"assets/file-4",
"assets/file-5",
"assets/file-6",
"assets/file-7",
"assets/file-8",
"--outfile=app",
],
cwd: String(dir),
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [, , buildExitCode] = await Promise.all([buildProc.stdout.text(), buildProc.stderr.text(), buildProc.exited]);
expect(buildExitCode).toBe(0);
// Run the compiled executable
await using runProc = Bun.spawn({
cmd: [String(dir) + "/app"],
cwd: String(dir),
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdout, , exitCode] = await Promise.all([runProc.stdout.text(), runProc.stderr.text(), runProc.exited]);
expect(stdout.trim()).toBe("IT WORKS 8");
expect(exitCode).toBe(0);
});