Trace the connections and flows between tapable hooks.
npm install tapable-tracerTrace the connections and flows between
tapable hooks in real-time.
Collect structured stack traces, and optionally export them as UML diagrams.
- Overview
- Features
- Installation
- Usage
- Initialize Tracer
- Register Hooks
- Export Frames
- Generate UML
- Tracer Internals
- Configuration
- Global Options
- Per-Hook Options
- Technical Details
- Examples
- License
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.
srcset="./assets/webpack-graph-dark.svg"
/>
alt="Webpack hook graph generated via tapable-tracer"
src="./assets/webpack-graph-light.svg"
/>
View the full interactive version here:
- Mermaid Chart Playground
- Mermaid Live Editor
- GitHub Mermaid Preview
- 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.
``sh`
yarn add tapable-tracer
To start tracing hooks, first create a tracer:
`ts
import { createTracer } from "tapable-tracer";
const tracer = createTracer();
`
To capture hook activity, register each hook with the tracer:
`ts
import { traceHook } from "tapable-tracer";
traceHook(tracer, hook1);
traceHook(tracer, hook2);
`
Export the captured frames as encodable array:
`ts
import { dumpStackTrace } from "tapable-tracer";
const frames = dumpStackTrace(tracer.trace);
`
Generate a Mermaid-compatible diagram code:
`ts
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.fnTap.fn
called.
- PostCallHook: After a
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.HookLabellerFunction
- labelHook ():TapLabellerFunction
Function to label hooks.
- labelTap ():
Function to label taps.
Pass HookTracingOptions to
traceHook():
The available options are:
- includeTrigger (boolean): Whether to include the trigger in the trace.string
- key (): 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 actual Tap.fn.CallFrame
- : A Tap.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:
1. Stack: A stack of CallSite objects that represents the current call stack.
2. 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:
1. A CallSite object is created for further reference.TapFrame
2. A is created and pushed onto the trace list.tap.fn
3. The function is overridden to capture the call events, by keepingCallSite
the object in the closure.
When a call or loop event occurs:
1. Create and push a TriggerFrame onto the trace list, if theincludeTrigger
options is set to true for the hook and theCallSite
call was caused by a tap.
2. Push the object onto the stack.CallSite
3. Execute the original callback function.
4. Pop the object from the stack.CallFrame
5. Create and push a onto the trace list.
Example: Output without triggers
`ts`
[
{ 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
srcset="./assets/hook-graph-dark.svg"
/>
alt="Hook graph generated via tapable-tracer"
src="./assets/hook-graph-light.svg"
/>
Example: Output with triggers
`ts``
[
{ 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
srcset="./assets/plugin-graph-dark.svg"
/>
alt="Plugin graph generated via tapable-tracer"
src="./assets/plugin-graph-light.svg"
/>
This project is licensed under the
MIT License.
See the LICENSE file for details.