esbuild icon indicating copy to clipboard operation
esbuild copied to clipboard

Coarse sourcemap segments in esbuild may break accurate mapping when chaining with terser

Open TrickyPi opened this issue 7 months ago • 3 comments

https://stackblitz.com/edit/stackblitz-starters-g4hjw9j1?file=index.js

Hi esbuild maintainers,

I've recently noticed that the sourcemaps generated by esbuild differ in granularity compared to tools like SWC and Babel. You can see this in the screenshot below — for example, in esbuild's sourcemap, the column for a return statement is set to 0 instead of the more accurate position column 2.

Image

This can lead to problems when combining sourcemap chains in bundle tools. For example, in a React project that uses a pipeline like esbuild -> terser, the final sourcemap from terser may fail to map correctly back to the original code due to the coarse segments from esbuild.

I’d like to ask if it's possible for esbuild to support more precise sourcemap segment positions, similar to what SWC and Babel provide. This would greatly help in improving tooling compatibility and debugging accuracy in complex build chains.

Thanks!

TrickyPi avatar May 22 '25 14:05 TrickyPi

This behavior is deliberately done by esbuild so that a) all of the generated code has source map coverage and b) mapping a debugger breakpoint at the beginning of the line to the original source code ends up in the right place (instead of on the previous line).

This can lead to problems when combining sourcemap chains in bundle tools. For example, in a React project that uses a pipeline like esbuild -> terser, the final sourcemap from terser may fail to map correctly back to the original code due to the coarse segments from esbuild.

This sounds like the actual problem that you're having, but you haven't provided any way for me to reproduce your problem. Can you provide reproduction instructions that demonstrate your problem?

evanw avatar May 27 '25 02:05 evanw

Thanks for your reply! Sorry for the delay — I finally had time to put together a reproduction. You can run or take a look at merge.js in the linked repo https://stackblitz.com/edit/stackblitz-starters-g4hjw9j1?file=merge.js to reproduce the issue.

Image

TrickyPi avatar Jun 02 '25 09:06 TrickyPi

You say this in your source code:

// Of course, we could improve this fallback logic by finding the closest segment where segment[0] is less than `sourceCol`,
// which would allow us to trace back to the correct esbuild segment [ 0, 0, 7, 2 ], but I'm not sure if there are other edge cases

My impression is that this is how chaining source maps is supposed to work: that the mapping covers everything at that position and afterward until either the next mapping or the end of the line.

However, I can reproduce the behavior that I think you're describing with rollup-plugin-sourcemaps, which (although seemingly abandoned) is probably a plugin that a lot of other people are using. With that plugin (even without terser) I get this, which is an incorrect mapping. There's also a solid argument that esbuild should be doing what other tools do if esbuild is the only one that's different, regardless of other considerations. So I'll consider changing this behavior in a future release.

Marking this issue as breaking since changing this behavior could break tools that depend on it, so this should be done in a breaking change release.

evanw avatar Jun 03 '25 02:06 evanw