diff --git a/src/bundler/linker_context/postProcessJSChunk.rs b/src/bundler/linker_context/postProcessJSChunk.rs index 84b494923e8e..7490f9eda43f 100644 --- a/src/bundler/linker_context/postProcessJSChunk.rs +++ b/src/bundler/linker_context/postProcessJSChunk.rs @@ -515,7 +515,13 @@ pub fn post_process_js_chunk( } // Add @bun comments and CJS wrapper start for each chunk when targeting Bun. - let is_bun = c.graph.ast.items_target()[chunk.entry_point.source_index() as usize].is_bun(); + // The pragma promises Latin-1-safe bytes for the whole chunk, so gate on + // every signal that any part was printed without ASCII-only escaping. + let is_bun = c.options.target.is_bun() + && c.graph.ast.items_target()[chunk.entry_point.source_index() as usize].is_bun() + && !chunk + .flags + .contains(crate::chunk::Flags::IS_BROWSER_CHUNK_FROM_SERVER_BUILD); if is_bun { if c.options.generate_bytecode_cache && output_format == options::OutputFormat::Cjs { const INPUT: &[u8] = diff --git a/test/bundler/bundler_bun.test.ts b/test/bundler/bundler_bun.test.ts index 674da11059bb..42e11458dffe 100644 --- a/test/bundler/bundler_bun.test.ts +++ b/test/bundler/bundler_bun.test.ts @@ -137,6 +137,64 @@ error: Hello World`, }, run: { stdout: "" }, }); + for (const target of ["node", "browser"] as const) { + itBundled(`bun/HashbangBunNoPragmaFor_${target}`, { + target, + files: { + "/entry.ts": `#!/usr/bin/env bun\nimport { Café } from "./lib.ts";\nconsole.log(new Café().méth());\n`, + "/lib.ts": `export class Café { méth() { return 7 } }\n`, + }, + onAfterBundle(api) { + const out = api.readFile("/out.js"); + expect(out).toStartWith("#!/usr/bin/env bun\n"); + expect(out).not.toContain("// @bun"); + }, + run: { stdout: "7" }, + }); + } + itBundled("bun/HashbangBunPragmaForBunTarget", { + target: "bun", + files: { + "/entry.ts": `#!/usr/bin/env bun\nimport { Café } from "./lib.ts";\nconsole.log(new Café().méth());\n`, + "/lib.ts": `export class Café { méth() { return 7 } }\n`, + }, + onAfterBundle(api) { + const out = api.readFile("/out.js"); + expect(out).toStartWith("#!/usr/bin/env bun\n// @bun\n"); + expect(out).not.toContain("méth"); + }, + run: { stdout: "7" }, + }); + for (const target of ["node", "browser"] as const) { + itBundled(`bun/HashbangBunNoCjsWrapperFor_${target}`, { + target, + format: "cjs", + files: { + "/entry.ts": `#!/usr/bin/env bun\nconsole.log("ALIVE", 42);\n`, + }, + onAfterBundle(api) { + const out = api.readFile("/out.js"); + expect(out).toStartWith("#!/usr/bin/env bun\n"); + expect(out).not.toContain("@bun"); + expect(out).not.toContain("(function(exports, require, module, __filename, __dirname)"); + }, + run: target === "node" ? { runtime: "node", stdout: "ALIVE 42" } : undefined, + }); + } + itBundled("bun/HashbangBunCjsWrapperForBunTarget", { + target: "bun", + format: "cjs", + files: { + "/entry.ts": `#!/usr/bin/env bun\nconsole.log("ALIVE", 42);\n`, + }, + onAfterBundle(api) { + const out = api.readFile("/out.js"); + expect(out).toStartWith( + "#!/usr/bin/env bun\n// @bun @bun-cjs\n(function(exports, require, module, __filename, __dirname) {", + ); + }, + run: { stdout: "ALIVE 42" }, + }); if (Bun.version.startsWith("1.4") || Bun.version.startsWith("1.3") || Bun.version.startsWith("1.2")) { for (const backend of ["api", "cli"] as const) { itBundled("bun/ExportsConditionsDevelopment" + backend.toUpperCase(), {