Skip to content

ertgl/tapable-tracer

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tapable-tracer

Trace the connections and flows between tapable hooks in real-time.

Collect structured stack traces, and optionally export them as UML diagrams.

Table of Contents

Overview

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.

Webpack hook graph generated via tapable-tracer

View the full interactive version here:

Features

  • 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.

Installation

yarn add tapable-tracer

Usage

Initialize Tracer

To start tracing hooks, first create a tracer:

import { createTracer } from "tapable-tracer";

const tracer = createTracer();

Register Hooks

To capture hook activity, register each hook with the tracer:

import { traceHook } from "tapable-tracer";

traceHook(tracer, hook1);
traceHook(tracer, hook2);

Export Frames

Export the captured frames as encodable array:

import { dumpStackTrace } from "tapable-tracer";

const frames = dumpStackTrace(tracer.trace);

Generate UML

Generate a Mermaid-compatible diagram code:

import { generateMermaidUML } from "tapable-tracer/extensions/mermaid";

const uml = generateMermaidUML(frames);

Tracer Internals

tapable-tracer exposes its own hooks (via tapable) for further instrumentation:

Configuration

Global Options

Pass TracerOptions to createTracer():

The available options are:

Per-Hook Options

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.

Technical Details

The tracer captures three different frame types:

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.
  2. A TapFrame is created and pushed onto the trace list.
  3. The tap.fn function is overridden to capture the call events, by keeping the CallSite object in the closure.

When a call or loop event occurs:

  1. Create and push a TriggerFrame onto the trace list, if the includeTrigger options is set to true for the hook and the call was caused by a tap.
  2. Push the CallSite object onto the stack.
  3. Execute the original callback function.
  4. Pop the CallSite object from the stack.
  5. Create and push a CallFrame onto the trace list.

Examples

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 Hook graph generated via tapable-tracer
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 Plugin graph generated via tapable-tracer

License

This project is licensed under the MIT License. See the LICENSE file for details.