Skip to content

Commit b48e10b

Browse files
authored
chore: implement stack trace formatting for console messages (#738)
The PR only implements the formatting, we don't create `DevTools.StackTrace` instances from puppeteer console message stack traces yet.
1 parent 6576c0f commit b48e10b

4 files changed

Lines changed: 95 additions & 0 deletions

File tree

src/formatters/consoleFormatter.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface ConsoleMessageData {
1515
count?: number;
1616
description?: string;
1717
args?: string[];
18+
stackTrace?: DevTools.StackTrace.StackTrace.StackTrace;
1819
}
1920

2021
// The short format for a console message, based on a previous format.
@@ -46,6 +47,7 @@ export function formatConsoleEventVerbose(
4647
`ID: ${msg.consoleMessageStableId}`,
4748
`Message: ${msg.type}> ${aggregatedIssue ? formatIssue(aggregatedIssue, msg.description, context) : msg.message}`,
4849
aggregatedIssue ? undefined : formatArgs(msg),
50+
formatStackTrace(msg.stackTrace),
4951
].filter(line => !!line);
5052
return result.join('\n');
5153
}
@@ -163,3 +165,42 @@ export function formatIssue(
163165
if (result.length === 0) return 'No affected resources found';
164166
return result.join('\n');
165167
}
168+
169+
function formatStackTrace(
170+
stackTrace: DevTools.StackTrace.StackTrace.StackTrace | undefined,
171+
): string {
172+
if (!stackTrace) {
173+
return '';
174+
}
175+
176+
return [
177+
'### Stack trace',
178+
formatFragment(stackTrace.syncFragment),
179+
...stackTrace.asyncFragments.map(formatAsyncFragment),
180+
].join('\n');
181+
}
182+
183+
function formatFragment(
184+
fragment: DevTools.StackTrace.StackTrace.Fragment,
185+
): string {
186+
return fragment.frames.map(formatFrame).join('\n');
187+
}
188+
189+
function formatAsyncFragment(
190+
fragment: DevTools.StackTrace.StackTrace.AsyncFragment,
191+
): string {
192+
const separatorLineLength = 40;
193+
const prefix = `--- ${fragment.description || 'async'} `;
194+
const separator = prefix + '-'.repeat(separatorLineLength - prefix.length);
195+
return separator + '\n' + formatFragment(fragment);
196+
}
197+
198+
function formatFrame(frame: DevTools.StackTrace.StackTrace.Frame): string {
199+
let result = `at ${frame.name ?? '<anonymous>'}`;
200+
if (frame.uiSourceCode) {
201+
result += ` (${frame.uiSourceCode.displayName()}:${frame.line}:${frame.column})`;
202+
} else if (frame.url) {
203+
result += ` (${frame.url}:${frame.line}:${frame.column})`;
204+
}
205+
return result;
206+
}

src/third_party/devtools.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
export type {
88
IssuesManagerEventTypes,
99
CDPConnection,
10+
StackTrace,
1011
} from '../../node_modules/chrome-devtools-frontend/mcp/mcp.js';
1112
export {
1213
AgentFocus,

tests/formatters/consoleFormatter.test.js.snapshot

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ exports[`consoleFormatter > formatConsoleEventShort > formats a console.log mess
1010
msgid=2 [log] Processing file: (1 args)
1111
`;
1212

13+
exports[`consoleFormatter > formatConsoleEventVerbose > formats a console message with a stack trace 1`] = `
14+
ID: 5
15+
Message: log> Hello stack trace!
16+
### Stack trace
17+
at foo (foo.ts:10:2)
18+
at bar (foo.ts:20:2)
19+
--- setTimeout -------------------------
20+
at schedule (util.ts:5:2)
21+
`;
22+
1323
exports[`consoleFormatter > formatConsoleEventVerbose > formats a console.error message 1`] = `
1424
ID: 4
1525
Message: error> Something went wrong

tests/formatters/consoleFormatter.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
formatConsoleEventShort,
1212
formatConsoleEventVerbose,
1313
} from '../../src/formatters/consoleFormatter.js';
14+
import type {DevTools} from '../../src/third_party/index.js';
1415
import {getMockAggregatedIssue} from '../utils.js';
1516

1617
describe('consoleFormatter', () => {
@@ -92,6 +93,48 @@ describe('consoleFormatter', () => {
9293
const result = formatConsoleEventVerbose(message);
9394
t.assert.snapshot?.(result);
9495
});
96+
97+
it('formats a console message with a stack trace', t => {
98+
const message: ConsoleMessageData = {
99+
consoleMessageStableId: 5,
100+
type: 'log',
101+
message: 'Hello stack trace!',
102+
args: [],
103+
stackTrace: {
104+
syncFragment: {
105+
frames: [
106+
{
107+
line: 10,
108+
column: 2,
109+
url: 'foo.ts',
110+
name: 'foo',
111+
},
112+
{
113+
line: 20,
114+
column: 2,
115+
url: 'foo.ts',
116+
name: 'bar',
117+
},
118+
],
119+
},
120+
asyncFragments: [
121+
{
122+
description: 'setTimeout',
123+
frames: [
124+
{
125+
line: 5,
126+
column: 2,
127+
url: 'util.ts',
128+
name: 'schedule',
129+
},
130+
],
131+
},
132+
],
133+
} as unknown as DevTools.StackTrace.StackTrace.StackTrace,
134+
};
135+
const result = formatConsoleEventVerbose(message);
136+
t.assert.snapshot?.(result);
137+
});
95138
});
96139

97140
it('formats a console.log message with issue type', t => {

0 commit comments

Comments
 (0)