From ef8148f7f25fba1b27dfce9223abe5d3506dcd58 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Thu, 9 Oct 2025 15:59:41 +0000 Subject: [PATCH] CRITICAL FIX: Register user-provided source maps in module loader The root cause was that when Bun transpiles files, it registers the source maps in vm.source_mappings via putMappings(). However, when a file is plain JavaScript with a user-provided sourcemap, Bun doesn't transpile it, so there's no cache entry, and putMappings() was never called. Solution: Added else-if blocks in ModuleLoader.zig after both putMappings() calls to check if --enable-source-maps is enabled. If it is and there's no cache entry, we now: 1. Check for sourceMappingURL comment in the source 2. Parse the source map (inline or external) 3. Register it in vm.source_mappings via putValue() This ensures that user-provided source maps are available when stack traces are formatted, allowing resolveSourceMapping() to find them. Locations modified: - ModuleLoader.zig:1192 - After first putMappings call - ModuleLoader.zig:2546 - After second putMappings call --- src/bun.js/ModuleLoader.zig | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/bun.js/ModuleLoader.zig b/src/bun.js/ModuleLoader.zig index 09921adc0a..cc909422b4 100644 --- a/src/bun.js/ModuleLoader.zig +++ b/src/bun.js/ModuleLoader.zig @@ -1189,6 +1189,39 @@ pub fn transpileSourceCode( break :brk ResolvedSource.Tag.javascript; }, }; + } else if (bun.sourcemap.JSSourceMap.@"--enable-source-maps") { + // When --enable-source-maps is enabled and there's no cache entry (i.e., file wasn't transpiled), + // check if the source has a user-provided sourceMappingURL and register it + const source_contents = source.contents; + if (bun.strings.lastIndexOfChar(source_contents, '\n')) |last_newline| { + const last_lines = source_contents[last_newline..]; + if (bun.strings.indexOf(last_lines, "//# sourceMappingURL=")) |url_idx| { + const url_start = last_newline + url_idx + "//# sourceMappingURL=".len; + const url_end = bun.strings.indexOfChar(source_contents[url_start..], '\n') orelse + (source_contents.len - url_start); + const source_map_url = bun.strings.trim(source_contents[url_start..][0..url_end], " \r\t"); + + // Use a stack fallback allocator for temporary parsing + var sfb = std.heap.stackFallback(8192, bun.default_allocator); + const temp_alloc = sfb.get(); + + // Try to parse the sourcemap URL (handles both inline and external) + const parse = SourceMap.parseUrl( + bun.default_allocator, + temp_alloc, + source_map_url, + .mappings_only, + ) catch null; + + if (parse) |p| { + if (p.map) |map| { + // Register the parsed source map + map.ref(); + jsc_vm.source_mappings.putValue(source.path.text, SavedSourceMap.Value.init(map)) catch {}; + } + } + } + } } const start_count = jsc_vm.transpiler.linker.import_counter; @@ -2510,6 +2543,36 @@ pub const RuntimeTranspilerStore = struct { }; return; + } else if (bun.sourcemap.JSSourceMap.@"--enable-source-maps") { + // When --enable-source-maps is enabled and there's no cache entry, + // check if the source has a user-provided sourceMappingURL and register it + const source_contents = parse_result.source.contents; + if (bun.strings.lastIndexOfChar(source_contents, '\n')) |last_newline| { + const last_lines = source_contents[last_newline..]; + if (bun.strings.indexOf(last_lines, "//# sourceMappingURL=")) |url_idx| { + const url_start = last_newline + url_idx + "//# sourceMappingURL=".len; + const url_end = bun.strings.indexOfChar(source_contents[url_start..], '\n') orelse + (source_contents.len - url_start); + const source_map_url = bun.strings.trim(source_contents[url_start..][0..url_end], " \r\t"); + + var sfb = std.heap.stackFallback(8192, bun.default_allocator); + const temp_alloc = sfb.get(); + + const parse = SourceMap.parseUrl( + bun.default_allocator, + temp_alloc, + source_map_url, + .mappings_only, + ) catch null; + + if (parse) |p| { + if (p.map) |map| { + map.ref(); + vm.source_mappings.putValue(parse_result.source.path.text, SavedSourceMap.Value.init(map)) catch {}; + } + } + } + } } if (parse_result.already_bundled != .none) {