How This Example Works
Compilers routinely both inline helper calls (calculateCustomsDuty flattened into dispatchPackage) and outline transpiled language constructs (such as extracting a block into a separate top-level function _outlinedCustomsBlock marked isStackFrame: true, isHidden: true with a definition OriginalScope), while injecting pure runtime helpers like __checkPositive (isStackFrame: true with no OriginalScope).
With scope-aware Logical Stepping, Chrome DevTools follows the authored control flow:
Step Over (F10) skips over inlined callees but steps through outlined blocks; Step Out (Shift+F11) exits an inlined function back to its caller or exits an outlined block all the way out of its enclosing authored function; and pure compiler helpers / unmapped glue are skipped automatically.
What to Try & Verify in DevTools
Try:Click "Run & Pause in Debugger" to pause at debugger; (line 22 of stepping.ts), then press Step Over (F10) three times.
- At the
debugger;:handlingFee,dutyAmountandtotalCostshow as<unavailable>(not initialized yet). - 1st
F10(Line 23const handlingFee = 15;): Stops on line 23 (__checkPositive(15)inbundle.jsis not entered). - 2nd
F10(Line 24const dutyAmount = calculateCustomsDuty(...)): Stops at the call site before entering the inlined range. - 3rd
F10(Skips Inlined Body → Line 28let totalCost = 0;): DevTools passes the inlined callee range inskipList, skipping all 3 statements ofcalculateCustomsDuty!
Try:Re-run, press F10 twice to reach line 24 (calculateCustomsDuty(...)), press Step Into (F11), then press Step Out (Shift+F11).
- Step Into (
F11) Enters Inlined Body: Execution moves to line 11 (const rate = ...) insidecalculateCustomsDuty, and the Call Stack adds the virtualcalculateCustomsDutyframe. - Pure Helper Skipped on
F11: Even thoughconst _inlinedArg = __checkPositive(val)runs first on that line,__checkPositivehas no original scope, soF11lands directly incalculateCustomsDuty. - Step Out (
Shift+F11) Returns to Caller: PressingShift+F11insidecalculateCustomsDutyskips its remaining lines (rawDuty,roundedDuty) and pauses back indispatchPackageon line 28 (let totalCost = 0;).
Try:From line 28 (let totalCost = 0;), press Step Over (F10) repeatedly through lines 30–36.
- Enters
_outlinedCustomsBlockAutomatically: Even though{ const clearance = ... }is extracted into a separate function_outlinedCustomsBlockinbundle.js,F10steps *into* line 30 (const clearance = openCustomsClearance(trackingId);) instead of skipping the call! - Merged Call Stack & Live Scopes: While paused inside the outlined block, the Call Stack shows
dispatchPackage(hiding_outlinedCustomsBlock), and the Scope pane shows bothBlock(clearance,subtotalWithDuty) anddispatchPackagevariables.totalCostis0until thetotalCost = ...line ran. - Seamless Exit: Pressing
F10afterclearance.commit(totalCost);skips the unmappedreturn tot;and pauses on line 36 (return { trackingId, dutyAmount, totalCost };).
Try:Re-run, step (F10) into line 31 (const subtotalWithDuty = ...) inside the outlined block, and press Step Out (Shift+F11).
- Exits the Whole Authored Function (
dispatchPackage): Instead of merely returning from_outlinedCustomsBlockback intodispatchPackage, DevTools unwinds both_outlinedCustomsBlockanddispatchPackage, pausing in the callerrunSteppingPipeline()(line 41)!
Other things to try
- Set breakpoints inside the outlined block and inside the inlined
calculateCustomsDuty, then re-run. - Step Into (
F11) thedispatchPackage(...)call inrunSteppingPipeline. - While paused inside the outlined block, use Restart frame on
dispatchPackage. - Select a non-top frame in the Call Stack and press
F10.
Not sure if it's a DevTools bug? Tab 4. Decoded Scopes shows what the source map actually encodes.
Try in the Console While Paused
While paused, select a frame and evaluate these in the Console (Esc opens the drawer) using the authored names:
{ trackingId, declaredValue, handlingFee, dutyAmount }
subtotalWithDuty + handlingFee