Part 3 · 3 chapters · ~25 min

Sources

Breakpoints of every kind and which stops the page least, the paused state with its live stack, scopes, stepping and the ignore list that makes framework sessions readable, and source maps and workspaces that turn DevTools from a view of the bundle into an editor of the code.

10

Breakpoints of every kind

the kinds, and when each is the least disruptive
  1. Line: click the gutter; inline markers for several statements on a line; "Deactivate breakpoints" keeps them idle. The Breakpoints pane lists all with checkboxes.
  2. Conditional: right-click → Add conditional breakpoint → an expression evaluated in the paused scope (item.id === 42, (window.n = (window.n || 0) + 1) > 100). Orange marker. The 43rd iteration without stepping 42 times.
  3. Logpoint: right-click → Add logpoint → an expression printed to the console each time, no pause, no file edit. Pink marker. A console.log in third-party code, removable without a deploy.
  4. Exceptions: pause on uncaught (the stack at the throw, before unwinding, which the console cannot give you); pause on caught too (noisy; finds what a catch swallowed).
  5. DOM, event listener, XHR/fetch (part 1): by mutation, by event category before the handler, by URL substring.
  6. Function: debug(fn) from the console pauses on entry; monitor(fn) logs calls. No source navigation.
the rule
Pause by place, by data, by event, by error or by function, whichever stops the page least often while still catching the moment. Logpoints never pause and are the first thing to try in code you do not own.
BREAKPOINTS OF EVERY KIND
line, conditional, logpoint, DOM, listener, XHR, exception, and the ones that fire on a function
swipe the figure sideways, or tap expand for full screen
1/6
line
Line breakpoints: click a gutter line number. Multiple breakpoints on one line are possible when it has several statements or an arrow function (the inline markers after setting one). Deactivate all (the toggle) keeps them but ignores them; the Breakpoints pane lists them with checkboxes and lets you remove by file.
11

Paused: the stack, the scope, stepping and the ignore list

what you see
  1. Call stack, live: your frame on top, framework frames beneath, then async frames across an await or a timer (reconstructed; the JS course part 10 explains how). Click a frame to move scope and source to it; "Restart frame" re-runs it with the current arguments.
  2. Scope: Local, Closure (named by the enclosing function), Module, Global, this. A variable you expect but cannot see was optimised away or never captured. Watch expressions re-evaluate on every step; hovering a name in the source shows its value.
what you do
  1. Step over (F10) runs the line including its calls; step into (F11) enters the call, and follows an await to its continuation; step out (⇧F11) runs to the return; step (F9) goes to the next statement anywhere; resume (F8). "Continue to here" is a one-shot breakpoint from the gutter menu.
  2. Ignore list (right-click a file → add to ignore list; patterns in Settings: /node_modules/, vendor chunks): ignored frames are hidden from the stack and stepped over. Step into your handler lands in your next line rather than in react-dom's scheduler. Sessions in a framework are unreadable without this.
  3. The paused console evaluates in the frame: inspect, assign to a variable, resume. Live edit: ⌘S in Sources swaps a function body without a reload (module scope and running closures keep the old code).
  4. Pretty print ({ }) formats minified code when no map is loaded.
go to the lab
  1. /loop/ordering: set a line breakpoint on the log('C: microtask') line by opening the evaluated code (it appears as a VM file in Sources when it runs; add a debugger statement in the editor to land there). Read the async stack: the Promise.then continuation with its "await"-style separator.
  2. /storage/idb-await-trap: enable Pause on uncaught exceptions and run the trap. The pause lands on the second put with tx in scope; evaluate tx in the console and read its state. Add the lab's source folder as a workspace first, so the pause shows the .tsx.
  3. /nav/spa-router: ignore-list react-dom (the vendor chunk), set a listener breakpoint on Mouse → click, click a link, and step into until you reach the nav function. Count how many frames the ignore list removed.
PAUSED: STEPPING, SCOPE, THE STACK AND BLACKBOXING
what you can see and do while the engine waits
swipe the figure sideways, or tap expand for full screen
1/6
call stack
The call stack while paused inside a React click handler: your handler at the top, then react-dom's callCallback, invokeGuardedCallback, dispatchEvent frames, then (grey, separated by "await" or "setTimeout") the async frames that led to the current task. Click any frame to see its scope and source; the stack is live, not a copy.
12

Source maps and workspaces

source maps
  1. What one is: a JSON file with sources, optional sourcesContent, names and mappings (base64 VLQ segments: per generated position, the delta to the original file, line, column and name). Loaded from //# sourceMappingURL or a SourceMap header. The Build Your Own course writes an encoder.
  2. What it gives: breakpoints in the .tsx translate to bundle positions; stacks, console errors and Performance flame charts name original files and functions.
  3. When it is wrong: stale content (an old map), breakpoints that never hit (a double transform whose maps were not composed), a greyed file ("source map could not be loaded": a 404 on the .map, CORS, or disabled in Settings → Sources). Check Network for the .map request first.
  4. In production: hidden maps (no comment in the bundle) uploaded to the error-reporting service symbolicate stacks server-side without shipping source to users; DevTools can load one manually (right-click → Add source map).
workspaces and overrides
  1. Workspace: Sources → Filesystem → Add folder; DevTools matches files by path and content (green dot). Edits in Sources (⌘S) and in Styles write to disk; with a dev server watching, a Styles tweak becomes a saved change and a hot reload.
  2. Overrides: the other direction: serve a local copy of any resource instead of the network's, to test a fix on a site you do not control or to stub a response.
  3. The setup the course assumes: a dev server with maps, the ignore list for node_modules, a workspace on the project, pause on uncaught exceptions. A throw lands in your file with your variables in scope; the fix is typed and saved; the page reloads.
go to the lab
  1. Add modules/devtools/lab as a workspace. Open src/routes/runtime.tsx in Sources (the maps are shipped in dist/) and set a breakpoint inside IdbAwaitTrap's run function; run /storage/idb-await-trap and confirm it pauses in the .tsx.
  2. In Network, find the .map request for the lab's bundle; block it (right-click → Block request URL), reload, and read what Sources shows instead; unblock.
SOURCE MAPS AND WORKSPACES
from the bundle back to your file, and from DevTools back to disk
swipe the figure sideways, or tap expand for full screen
1/6
the map
The map: bundle.js ends with //# sourceMappingURL=bundle.js.map (or the server sends a SourceMap header). The map has sources (original paths), sourcesContent (optionally the original text, so DevTools needs no server for it), names, and mappings: a base64 VLQ string encoding, per generated segment, the delta to the original position. DevTools decodes it once and shows the originals in the tree under their paths.