EXAMPLE 05 • stepping.ts

Logical Stepping Across Inlined & Outlined Functions

Step Over, Into, and Out of inlined helper functions and outlined transpiled blocks while automatically skipping compiler helpers.

Multi-Statement Inlined Range (calculateCustomsDuty)Outlined Function Range (isStackFrame: true, isHidden: true + OriginalScope)Pure Compiler Helper Ranges (isStackFrame: true, no OriginalScope)Unmapped Compiler Glue Auto-Stepping
OPEN CHROME DEVTOOLS (F12) FIRST, THEN TRIGGER:
Ready. Click "Run & Pause in Debugger" with DevTools open.

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

Logical Stepping

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, dutyAmount and totalCost show as <unavailable> (not initialized yet).
  • 1st F10 (Line 23 const handlingFee = 15;): Stops on line 23 (__checkPositive(15) in bundle.js is not entered).
  • 2nd F10 (Line 24 const dutyAmount = calculateCustomsDuty(...)): Stops at the call site before entering the inlined range.
  • 3rd F10 (Skips Inlined Body → Line 28 let totalCost = 0;): DevTools passes the inlined callee range in skipList, skipping all 3 statements of calculateCustomsDuty!
Logical Stepping

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 = ...) inside calculateCustomsDuty, and the Call Stack adds the virtual calculateCustomsDuty frame.
  • Pure Helper Skipped on F11: Even though const _inlinedArg = __checkPositive(val) runs first on that line, __checkPositive has no original scope, so F11 lands directly in calculateCustomsDuty.
  • Step Out (Shift+F11) Returns to Caller: Pressing Shift+F11 inside calculateCustomsDuty skips its remaining lines (rawDuty, roundedDuty) and pauses back in dispatchPackage on line 28 (let totalCost = 0;).
Logical Stepping

Try:From line 28 (let totalCost = 0;), press Step Over (F10) repeatedly through lines 30–36.

  • Enters _outlinedCustomsBlock Automatically: Even though { const clearance = ... } is extracted into a separate function _outlinedCustomsBlock in bundle.js, F10 steps *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 both Block (clearance, subtotalWithDuty) and dispatchPackage variables. totalCost is 0 until the totalCost = ... line ran.
  • Seamless Exit: Pressing F10 after clearance.commit(totalCost); skips the unmapped return tot; and pauses on line 36 (return { trackingId, dutyAmount, totalCost };).
Logical Stepping

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 _outlinedCustomsBlock back into dispatchPackage, DevTools unwinds both _outlinedCustomsBlock and dispatchPackage, pausing in the caller runSteppingPipeline() (line 41)!
Other things to try
  • Set breakpoints inside the outlined block and inside the inlined calculateCustomsDuty, then re-run.
  • Step Into (F11) the dispatchPackage(...) call in runSteppingPipeline.
  • 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 }
Expected Result: { trackingId: "SHP-9042", declaredValue: 450, handlingFee: 15, dutyAmount: 54 }
Works in dispatchPackage once dutyAmount is initialized (before that, handlingFee/dutyAmount are unavailable) and while paused inside _outlinedCustomsBlock via its dispatchPackage definition scope bindings.
subtotalWithDuty + handlingFee
Expected Result: 519 (when paused inside the outlined block after line 31)
Combines subtotalWithDuty (sub in customsBlock) with handlingFee (fee in dispatchPackage).