Feature Request: Source-Map/Source Out Of Sync Detection
Many people don't trust source maps (@alexdima), so they always prefer stepping through the transpiled JS to rule out a bunch of issues.
These issues include:
tsc/webpack/gulpdies silently. You change a TypeScript file, restart/reload your app/test/website and debug again. However, the executed JavaScript source code no longer matches the TypeScript source code. You think you fixed the bug, step through the fixed code, but it is still broken and nothing makes sense anymore.tsc/webpack/gulpis too slow: You reload/restart before your build tool is finished. This causes the same issues as described in 1.- You use some hot-reload technology (like webpack HMR) that allows to load different (i.e. updated) JavaScript modules that map to the same TypeScript file path. It can happen that you step through both the new and the old module. Currently, you will see the same TypeScript file which is clearly problematic when stepping through code provided by the old module.
It would be really nice if vscode-js-debug detects if the file on disk is in sync with the code that is being executed, i.e. if the source map still applies. If they aren't in sync anymore, vscode-js-debug should either step through the js code ignoring the source map, or, if the TypeScript code is inlined into the source map, show a read-only editor of the TypeScript code at the time of the transpilation.
Maybe this can even be combined with a diff view.
This is hard to do precisely, but a (relatively) cheap way could be to see if the lines in the source mapped changes have the same number of lines as the original source. It would still require reading the file off disk to verify contents, which we don't do today.
Seeing this issue makes me wish I had more thumbs so I could keep banging the 👍! Source vs. sourcemap mismatch is, by far, my biggest productivity problem with VSCode overall. Even if sourcemaps are only mismatched 20% of the time, when I'm in a debug session I never know when my breakpoints are correct or not, so I end up prophylactically restarting debug sessions far more often than I probably need to.
It's especially a pain in my main project where I have two different hot-reloading debug sessions (one React for front-end, one serverless-offline for back-end code), and where reloading the debug session requires three separate steps: npm start, start Chrome debug session, and start Node debug session. This takes 60-90 seconds. Painful!
FWIW, I've learned from trial and error that:
- When I save a source file while the debugger is stopped at a breakpoint, it almost always breaks the sourcemap/source synchrony after execution resumes and hot-reloads happen. Resuming and then saving sometimes works I think, but honestly I still don't understand the cases where it works vs. doesn't.
- After stopping and starting multiple debug sessions, the debugger seems to get "stale" and stop syncing sourcemaps vs. code. When I see this behavior, I close and re-open all Node processes, and sometimes even have to reboot. It seems that having Chrome devtools open outside VS may make this more likely, but I can't repro reliably.
BTW, another benefit of having UI to detect this case is that it will become easier to repro and fix the underlying problems causing these mismatches in the first place. Currently, the problem happens and then sometime later I discover that my breakpoints and stepping are busted. So I don't know what actually broke my debugging. Nor do 1,000s of other developers facing the same problems. If everyone could see the problem happen in real time after they do X or Y actions, then the ecosystem would fix it much faster.
Here's a whole bunch of UI ideas around this problem. Hope this is useful!
- When I'm viewing a source file while a debug session is active, if there's a sourcemap mismatch there'd be a big red non-modal warning shown somewhere
- Ideally that same warning UI would give me the ability to switch to view the actual code being executed (the transpiled code), so that if I'm knee-deep in a debug session I can at least continue debugging into the transpiled code instead of having to restart from scratch. BTW, is there a way to do this today? If not, this would be a valuable feature in its own right.
- Ideally the warning UI would also have a "fix" button which would do something which might be too slow to do automatically, so the user has to opt in, e.g. fully reloading every sourcemap or something like that. I'm assuming that fixing is actually possible in some cases, which may be an over-optimistic assumption.
- In a perfect world, the "fix" button could be attached to a custom action or script in launch.json in case there's a way to reload or rebuild or otherwise fix the problem in a particular framework.
- Could this UI also have a "Restart all Debug Sessions" button which would stop all debug sessions, perform whatever cleanup is needed, and then restart the sessions? This would save me a lot of clicks because doing those steps manually is what I have to do many times per day.
- Maybe the warning UI could link to the cool new Breakpoints Diagnostics command?
- Maybe the warning UI could also link to a docs page which would explain how to avoid sourcemap issues in various popular frameworks, including Node, React, Next.js, severless-offline, Angular, etc., and popular bundlers like webpack and rollup. even if the docs are minimal at first, having one place where lots of users go to solve mismatch issues will be a honeypot for contributors to PR improvements to those docs.
- If I'm stopped in the debugger and the code is a mismatch, then the focused line should not be green or yellow-- maybe make it light orange or light-red background or cross-hatched or some other indication that the code you're looking at is bogus?
- Could there be some sort of split-screen view where you see the transpiled code for the line underneath the original source, kinda like what VSCode does for exceptions sometimes? Or a "Peek Transpiled Code" like we do for declarations or references? I realize there's not a 1:1 mapping, but merely showing the current line in the transpiled file with 2 lines above and 2 lines below would be super-useful.
- If the file I'm looking at has a breakpoint circle showing in the window, but the file has a source vs. map mismatch, then the breakpoint circle would ideally look different as a hint that it may be bogus, like maybe a small white question mark on top of it, or a white line or X through it?
- The breakpoints pane would show the same icon, and also show the line number with an orange or red background color instead of gray if it's in a file with a sourcemap mismatch.
- Given that saving code while I'm stopped in the debugger seems to cause the worst issues, perhaps there could be a UI warning (opt-in in launch.json?) that would tell me to resume execution before trying to save? I always forget to resume before saving and it'd be helpful to have an automated reminder.
Note that there are actually two kinds of mismatch conditions:
- Mismatches caused by originally-bundled code being wrong. This is a build issue, and debugging those files will always be broken. The only way to fix it is for developers to fix their build steps, launch.json
sourceMapPathOverrides, or hassle upstream package authors to fix their busted sourcemaps. - Mismatches caused by hot-reloading during a debug session. This is what I run into most often, and it's the most annoying because it's unpredictable. But it's also easily fixable by restarting the debug session (or, in worst cases, restarting VSCode and/or Chrome and/or the OS).
I'm not sure if it's possible to differentiate these two cases, but it seems worthwhile to try, e.g. if a mismatch wasn't there when the debug session started, but is there now, then show slightly different UI to indicate that a restart of the session will fix the problem?
Thanks again for working to improve this debug experience. It's appreciated! 🙏
One more obvious idea: another low-cost way to detect a mismatch would be to see if breakpoints are now located in places in the current line of code that are impossible given the AST of the currently-viewed code, like inside a variable name or in the middle of whitespace. A breakpoint that's not at the start of the line or the start of an AST token is a clear sign that a sourcemap vs. sourcemap mismatch is present.