diff --git a/src/bun.js/ModuleLoader.zig b/src/bun.js/ModuleLoader.zig index 5b4a2f1b0b..da56b70edc 100644 --- a/src/bun.js/ModuleLoader.zig +++ b/src/bun.js/ModuleLoader.zig @@ -1140,14 +1140,14 @@ export fn Bun__runVirtualModule(globalObject: *JSGlobalObject, specifier_ptr: *c fn getHardcodedModule(jsc_vm: *VirtualMachine, specifier: bun.String, hardcoded: HardcodedModule) ?ResolvedSource { analytics.Features.builtin_modules.insert(hardcoded); return switch (hardcoded) { - .@"bun:main" => .{ + .@"bun:main" => if (jsc_vm.entry_point.generated) .{ .allocator = null, .source_code = bun.String.cloneUTF8(jsc_vm.entry_point.source.contents), .specifier = specifier, .source_url = specifier, .tag = .esm, .source_code_needs_deref = true, - }, + } else null, .@"bun:internal-for-testing" => { if (!Environment.isDebug) { if (!is_allowed_to_use_internal_testing_apis) diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index de04f1ff67..9cf259066b 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -1616,7 +1616,7 @@ fn _resolve( if (strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) { ret.path = Runtime.Runtime.Imports.Name; return; - } else if (strings.eqlComptime(specifier, main_file_name)) { + } else if (strings.eqlComptime(specifier, main_file_name) and jsc_vm.entry_point.generated) { ret.result = null; ret.path = jsc_vm.entry_point.source.path.text; return; diff --git a/src/bundler/entry_points.zig b/src/bundler/entry_points.zig index 8e0db5e6b2..aa6776ec89 100644 --- a/src/bundler/entry_points.zig +++ b/src/bundler/entry_points.zig @@ -150,6 +150,7 @@ pub const ClientEntryPoint = struct { pub const ServerEntryPoint = struct { source: logger.Source = undefined, + generated: bool = false, pub fn generate( entry: *ServerEntryPoint, @@ -230,6 +231,7 @@ pub const ServerEntryPoint = struct { entry.source = logger.Source.initPathString(name, code); entry.source.path.text = name; entry.source.path.namespace = "server-entry"; + entry.generated = true; } }; diff --git a/test/js/bun/http/bun-serve-html-entry.test.ts b/test/js/bun/http/bun-serve-html-entry.test.ts index 34c316a16f..b2f590621e 100644 --- a/test/js/bun/http/bun-serve-html-entry.test.ts +++ b/test/js/bun/http/bun-serve-html-entry.test.ts @@ -634,3 +634,42 @@ test.concurrent("bun serve files with correct Content-Type headers", async () => // The process will be automatically cleaned up by 'await using' } }); + +test("importing bun:main from HTML entry preload does not crash", async () => { + const dir = tempDirWithFiles("html-entry-bun-main", { + "index.html": /*html*/ ` + + + Test +

Hello

+ + `, + "preload.mjs": /*js*/ ` + try { + await import("bun:main"); + } catch {} + // Signal that preload ran successfully without crashing + console.log("PRELOAD_OK"); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "--preload", "./preload.mjs", "index.html", "--port=0"], + env: bunEnv, + cwd: dir, + stdout: "pipe", + stderr: "pipe", + }); + + const decoder = new TextDecoder(); + let text = ""; + for await (const chunk of proc.stdout) { + text += decoder.decode(chunk, { stream: true }); + if (text.includes("http://")) break; + } + + expect(text).toContain("PRELOAD_OK"); + + proc.kill(); + await proc.exited; +});