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-tracerTo 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.fncalled. - PostCallHook: After a
Tap.fncompletes. - 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.fnis 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
CallSiteobjects 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
CallSiteobject is created for further reference. - A
TapFrameis created and pushed onto thetracelist. - The
tap.fnfunction is overridden to capture the call events, by keeping theCallSiteobject in the closure.
When a call or loop event occurs:
- Create and push a
TriggerFrameonto thetracelist, if theincludeTriggeroptions is set totruefor the hook and the call was caused by a tap. - Push the
CallSiteobject onto thestack. - Execute the original callback function.
- Pop the
CallSiteobject from thestack. - Create and push a
CallFrameonto thetracelist.
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.