Skip to content

Commit c43204c

Browse files
committed
chore: add docks
1 parent cba2218 commit c43204c

File tree

1 file changed

+40
-3
lines changed

1 file changed

+40
-3
lines changed

src/addons/consoleCatcher.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ const MAX_LOGS = 20;
1515
const CONSOLE_METHODS: string[] = ['log', 'warn', 'error', 'info', 'debug'];
1616

1717
/**
18-
* Console catcher class for intercepting and capturing console logs
18+
* Console catcher class for intercepting and capturing console logs.
19+
*
20+
* This singleton class wraps native console methods to capture all console output with accurate
21+
* stack traces. When developers click on console messages in DevTools, they are taken to the
22+
* original call site in their code, not to the interceptor's code.
1923
*/
2024
export class ConsoleCatcher {
2125
/**
@@ -178,7 +182,10 @@ export class ConsoleCatcher {
178182
}
179183

180184
/**
181-
* Initializes the console interceptor by overriding default console methods
185+
* Initializes the console interceptor by overriding default console methods.
186+
*
187+
* Wraps native console methods to intercept all calls, capture their context, and generate
188+
* accurate stack traces that point to the original call site (not the interceptor).
182189
*/
183190
// eslint-disable-next-line @typescript-eslint/member-ordering
184191
public init(): void {
@@ -193,13 +200,40 @@ export class ConsoleCatcher {
193200
return;
194201
}
195202

203+
// Store original function to forward calls after interception
196204
const oldFunction = window.console[method].bind(window.console);
197205

206+
/**
207+
* Override console method to intercept all calls.
208+
*
209+
* For each intercepted call, we:
210+
* 1. Generate a stack trace to find the original call site
211+
* 2. Format the console arguments into a structured message
212+
* 3. Create a ConsoleLogEvent with metadata
213+
* 4. Store it in the buffer
214+
* 5. Forward the call to the native console (so output still appears in DevTools)
215+
*/
198216
window.console[method] = (...args: unknown[]): void => {
217+
// Capture full stack trace
199218
const errorStack = new Error('Console log stack trace').stack;
200219
const stackLines = errorStack?.split('\n') || [];
201220

202-
// Skip first line (Error message) and find first stack frame outside of consoleCatcher module
221+
/**
222+
* Dynamic stack frame identification.
223+
*
224+
* Problem: Fixed slice(2) doesn't work reliably because the number of internal frames
225+
* varies based on code structure (arrow functions, class methods, TS→JS transforms, etc.).
226+
*
227+
* Solution: Find the first stack frame that doesn't belong to consoleCatcher module.
228+
* This ensures DevTools will navigate to the user's code, not the interceptor's code.
229+
*
230+
* Process:
231+
* 1. Skip the first line (Error message: "Error: Console log stack trace")
232+
* 2. Iterate through stack frames
233+
* 3. Find first frame that doesn't contain "consoleCatcher" in its path
234+
* 4. Use that frame as fileLine (clickable link in DevTools)
235+
* 5. Use all subsequent frames as the full stack trace
236+
*/
203237
const consoleCatcherPattern = /consoleCatcher/i;
204238
let userFrameIndex = 1; // Skip Error message line
205239

@@ -210,7 +244,9 @@ export class ConsoleCatcher {
210244
}
211245
}
212246

247+
// Extract user code stack (everything from the first non-consoleCatcher frame)
213248
const userStack = stackLines.slice(userFrameIndex).join('\n');
249+
// First frame is used as fileLine - this is what DevTools shows as clickable link
214250
const fileLine = stackLines[userFrameIndex]?.trim() || '';
215251
const { message, styles } = this.formatConsoleArgs(args);
216252

@@ -225,6 +261,7 @@ export class ConsoleCatcher {
225261
};
226262

227263
this.addToConsoleOutput(logEvent);
264+
// Forward to native console so output still appears in DevTools
228265
oldFunction(...args);
229266
};
230267
});

0 commit comments

Comments
 (0)