Trace the connections and flows between tapable hooks in real-time.
Collect structured stack traces, and optionally export them as UML diagrams.
Have you ever wondered what the internals of webpack look like?
Below is a UML-style representation of webpack's internal hooks captured at
runtime. This diagram was generated using the tapable-tracer
and its
webpack plugin by
tracing dynamically every hook tap and call in the system.
View the full interactive version here:
- Real-time: Observe hooks as they're tapped and called.
- Structured: Frames represent a directed graph.
- Dynamic: No patching or rewriting needed.
- UML Export: Visualize traces via Mermaid diagrams.
- Configurable: Include or exclude the triggers.
- Customizable: Embed information to be visible on diagrams.
- Universal: Works with any tapable-based code, not just webpack.
yarn add tapable-tracer
To start tracing hooks, first create a tracer:
import { createTracer } from "tapable-tracer";
const tracer = createTracer();
To capture hook activity, register each hook with the tracer:
import { traceHook } from "tapable-tracer";
traceHook(tracer, hook1);
traceHook(tracer, hook2);
Export the captured frames as encodable array:
import { dumpStackTrace } from "tapable-tracer";
const frames = dumpStackTrace(tracer.trace);
Generate a Mermaid-compatible diagram code:
import { generateMermaidUML } from "tapable-tracer/extensions/mermaid";
const uml = generateMermaidUML(frames);
tapable-tracer
exposes its own hooks (via tapable) for further
instrumentation:
- PreCallHook: Before a
Tap.fn
called. - PostCallHook: After a
Tap.fn
completes. - HandleStackFrameHook: When a new stack frame is emitted.
Pass TracerOptions
to
createTracer()
:
The available options are:
- interceptorName (
string
): Name of the interceptor to use. - labelHook (
HookLabellerFunction
): Function to label hooks. - labelTap (
TapLabellerFunction
): Function to label taps.
Pass HookTracingOptions
to
traceHook()
:
The available options are:
- includeTrigger (
boolean
): Whether to include the trigger in the trace. - key (
string
): The hook's identifier in a container data-structure inside the system. Also used as the fallback label for the hook.
The tracer captures three different frame types:
TapFrame
: A tap is registered to a hook.TriggerFrame
: A delegate is called before the actualTap.fn
.CallFrame
: ATap.fn
is called.
Additionally, it uses a CallSite
context
object per tap, for storing the hook, tap, and the original callback function.
To capture the frames the tracer uses two separate states:
- Stack: A stack of
CallSite
objects that represents the current call stack. - Trace: A list of frames that represents the entire trace of the flows.
For tracing a hook, the tracer intercepts the hook's tap
, call
and loop
events.
When a tap is added:
- A
CallSite
object is created for further reference. - A
TapFrame
is created and pushed onto thetrace
list. - The
tap.fn
function is overridden to capture the call events, by keeping theCallSite
object in the closure.
When a call
or loop
event occurs:
- Create and push a
TriggerFrame
onto thetrace
list, if theincludeTrigger
options is set totrue
for the hook and the call was caused by a tap. - Push the
CallSite
object onto thestack
. - Execute the original callback function.
- Pop the
CallSite
object from thestack
. - Create and push a
CallFrame
onto thetrace
list.
Example: Output without triggers
[
{ hook: 'hook1', tap: 'hook2', type: 'tap' },
{ hook: 'hook2', tap: 'hook3', type: 'tap' },
{ hook: 'hook3', tap: 'hook4', type: 'tap' },
{ callee: 'hook1', caller: null, type: 'call' },
{ callee: 'hook2', caller: 'hook1', type: 'call' },
{ callee: 'hook3', caller: 'hook2', type: 'call' },
{ callee: 'hook4', caller: 'hook3', type: 'call' }
]
Example: Graph visualization of the output without triggers
Example: Output with triggers
[
{ hook: 'hook1', tap: 'Plugin2', type: 'tap' },
{ hook: 'hook2', tap: 'Plugin3', type: 'tap' },
{ hook: 'hook3', tap: 'Plugin4', type: 'tap' },
{ callee: 'hook1', caller: null, type: 'call' },
{ callee: 'Plugin2', caller: 'hook1', type: 'trigger' },
{ callee: 'hook2', caller: 'Plugin2', type: 'call' },
{ callee: 'Plugin3', caller: 'hook2', type: 'trigger' },
{ callee: 'hook3', caller: 'Plugin3', type: 'call' },
{ callee: 'Plugin4', caller: 'hook3', type: 'trigger' },
{ callee: 'hook4', caller: 'Plugin4', type: 'call' }
]
Example: Graph visualization of the output with triggers
This project is licensed under the MIT License. See the LICENSE file for details.