From ab1d20e596b477c12bc8b49ed04ecc6ac47b664c Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Thu, 16 Oct 2025 03:50:56 +0000 Subject: [PATCH] Add bunfig.toml [runtime] sourceCodePreview option This adds a configuration option to disable source code previews in error stack traces via bunfig.toml: [runtime] sourceCodePreview = false When disabled, error stack traces will still show file paths and line numbers, but will not include the source code snippets with line numbers and caret indicators that are normally displayed. Changes: - Add source_code_preview field to RuntimeOptions in cli.zig - Add parsing for [runtime].sourceCodePreview in bunfig.zig - Add source_code_preview field to VirtualMachine struct and Options - Wire up the config to disable collectSourceLines() in remapZigException - Add test verifying source code preview can be disabled Generated with Claude Code Co-Authored-By: Claude --- src/bun.js.zig | 2 + src/bun.js/VirtualMachine.zig | 7 +- src/bunfig.zig | 10 +++ src/cli.zig | 1 + src/cli/test_command.zig | 1 + .../bun/runtime/source-code-preview.test.ts | 76 +++++++++++++++++++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 test/js/bun/runtime/source-code-preview.test.ts diff --git a/src/bun.js.zig b/src/bun.js.zig index 1a4fafbbd4..91040c11d7 100644 --- a/src/bun.js.zig +++ b/src/bun.js.zig @@ -37,6 +37,7 @@ pub const Run = struct { .args = ctx.args, .graph = graph_ptr, .is_main_thread = true, + .source_code_preview = ctx.runtime_options.source_code_preview, }), .arena = arena, .ctx = ctx, @@ -174,6 +175,7 @@ pub const Run = struct { .debugger = ctx.runtime_options.debugger, .dns_result_order = DNSResolver.Order.fromStringOrDie(ctx.runtime_options.dns_result_order), .is_main_thread = true, + .source_code_preview = ctx.runtime_options.source_code_preview, }, ), .arena = arena, diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index ea0095dfea..5dc12fe1ed 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -48,6 +48,7 @@ unhandled_pending_rejection_to_capture: ?*JSValue = null, standalone_module_graph: ?*bun.StandaloneModuleGraph = null, smol: bool = false, dns_result_order: DNSResolver.Order = .verbatim, +source_code_preview: bool = true, counters: Counters = .{}, hot_reload: bun.cli.Command.HotReload = .none, @@ -1086,6 +1087,7 @@ pub const Options = struct { /// Worker VMs are always destroyed on exit, regardless of this setting. Setting this to /// true may expose bugs that would otherwise only occur using Workers. destruct_main_thread_on_exit: bool = false, + source_code_preview: bool = true, }; pub var is_smol_mode = false; @@ -1180,6 +1182,7 @@ pub fn init(opts: Options) !*VirtualMachine { uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc_vm; vm.smol = opts.smol; vm.dns_result_order = opts.dns_result_order; + vm.source_code_preview = opts.source_code_preview; if (opts.smol) is_smol_mode = opts.smol; @@ -1330,6 +1333,7 @@ pub fn initWorker( } vm.smol = opts.smol; + vm.source_code_preview = opts.source_code_preview; vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler); vm.global = JSGlobalObject.create( @@ -1430,6 +1434,7 @@ pub fn initBake(opts: Options) anyerror!*VirtualMachine { vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler); vm.smol = opts.smol; + vm.source_code_preview = opts.source_code_preview; if (opts.smol) is_smol_mode = opts.smol; @@ -2923,7 +2928,7 @@ fn printErrorInstance( exception_list, &exception_holder.need_to_clear_parser_arena_on_deinit, &source_code_slice, - formatter.error_display_level != .warn, + formatter.error_display_level != .warn and this.source_code_preview, ); } const prev_had_errors = this.had_errors; diff --git a/src/bunfig.zig b/src/bunfig.zig index 2041454161..3a45c5f817 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -779,6 +779,16 @@ pub const Bunfig = struct { } } } + + if (json.get("runtime")) |runtime_expr| { + if (runtime_expr.get("sourceCodePreview")) |source_code_preview| { + if (source_code_preview.asBool()) |value| { + this.ctx.runtime_options.source_code_preview = value; + } else { + try this.addError(source_code_preview.loc, "Expected boolean"); + } + } + } } if (json.getObject("serve")) |serve_obj2| { diff --git a/src/cli.zig b/src/cli.zig index bccc5c29f1..ae2a04a51e 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -385,6 +385,7 @@ pub const Command = struct { expose_gc: bool = false, preserve_symlinks_main: bool = false, console_depth: ?u16 = null, + source_code_preview: bool = true, }; var global_cli_ctx: Context = undefined; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index 23b70343f8..f4958fae00 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -1380,6 +1380,7 @@ pub const TestCommand = struct { .smol = ctx.runtime_options.smol, .debugger = ctx.runtime_options.debugger, .is_main_thread = true, + .source_code_preview = ctx.runtime_options.source_code_preview, }, ); vm.argv = ctx.passthrough; diff --git a/test/js/bun/runtime/source-code-preview.test.ts b/test/js/bun/runtime/source-code-preview.test.ts new file mode 100644 index 0000000000..5b4b9616a9 --- /dev/null +++ b/test/js/bun/runtime/source-code-preview.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDir } from "harness"; + +describe("sourceCodePreview config option", () => { + test("default behavior shows source code in error stack traces", async () => { + using dir = tempDir("source-code-preview-default", { + "test.js": ` +function foo() { + console.log(new Error().stack); +} +foo(); + `, + }); + + const proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + cwd: String(dir), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + const stdout = await proc.stdout.text(); + const exitCode = await proc.exited; + + expect(exitCode).toBe(0); + // Should contain line numbers and source code + expect(stdout).toContain("test.js:"); + // Should contain the function call location with source code or line number + expect(stdout.length).toBeGreaterThan(10); + }); + + test("sourceCodePreview=false disables source code in error stack traces", async () => { + using dir = tempDir("source-code-preview-disabled", { + "bunfig.toml": ` +[runtime] +sourceCodePreview = false + `, + "test.fixture.ts": ` +function foo() { + console.log(new Error().stack); +} +foo(); + `, + }); + + const proc2 = Bun.spawn({ + cmd: [bunExe(), "test.fixture.ts"], + cwd: String(dir), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + const stdout = await proc2.stdout.text(); + const stderr = await proc2.stderr.text(); + const exitCode = await proc2.exited; + + expect(exitCode).toBe(0); + + const output = stdout + stderr; + // Should still contain file path and line numbers + expect(output).toContain("test.fixture.ts:"); + + // Should NOT contain source code snippets (no pipe characters from source display) + // The source code preview typically shows lines like: + // 3 | console.log(new Error().stack); + // ^ + // We check that these formatted source lines are not present + const lines = output.split("\n"); + const hasSourceCodeDisplay = lines.some( + line => + /^\s*\d+\s+\|/.test(line) || // Lines with line numbers and pipe + /^\s*\^/.test(line), // Caret indicators + ); + expect(hasSourceCodeDisplay).toBe(false); + }); +});