Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/formatters/consoleFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface ConsoleMessageData {
count?: number;
description?: string;
args?: string[];
stackTrace?: DevTools.StackTrace.StackTrace.StackTrace;
}

// The short format for a console message, based on a previous format.
Expand Down Expand Up @@ -46,6 +47,7 @@ export function formatConsoleEventVerbose(
`ID: ${msg.consoleMessageStableId}`,
`Message: ${msg.type}> ${aggregatedIssue ? formatIssue(aggregatedIssue, msg.description, context) : msg.message}`,
aggregatedIssue ? undefined : formatArgs(msg),
formatStackTrace(msg.stackTrace),
].filter(line => !!line);
return result.join('\n');
}
Expand Down Expand Up @@ -163,3 +165,42 @@ export function formatIssue(
if (result.length === 0) return 'No affected resources found';
return result.join('\n');
}

function formatStackTrace(
stackTrace: DevTools.StackTrace.StackTrace.StackTrace | undefined,
): string {
if (!stackTrace) {
return '';
}

return [
'### Stack trace',
formatFragment(stackTrace.syncFragment),
...stackTrace.asyncFragments.map(formatAsyncFragment),
].join('\n');
}

function formatFragment(
fragment: DevTools.StackTrace.StackTrace.Fragment,
): string {
return fragment.frames.map(formatFrame).join('\n');
}

function formatAsyncFragment(
fragment: DevTools.StackTrace.StackTrace.AsyncFragment,
): string {
const separatorLineLength = 40;
const prefix = `--- ${fragment.description || 'async'} `;
const separator = prefix + '-'.repeat(separatorLineLength - prefix.length);
return separator + '\n' + formatFragment(fragment);
}

function formatFrame(frame: DevTools.StackTrace.StackTrace.Frame): string {
let result = `at ${frame.name ?? '<anonymous>'}`;
if (frame.uiSourceCode) {
result += ` (${frame.uiSourceCode.displayName()}:${frame.line}:${frame.column})`;
} else if (frame.url) {
result += ` (${frame.url}:${frame.line}:${frame.column})`;
}
return result;
}
1 change: 1 addition & 0 deletions src/third_party/devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
export type {
IssuesManagerEventTypes,
CDPConnection,
StackTrace,
} from '../../node_modules/chrome-devtools-frontend/mcp/mcp.js';
export {
AgentFocus,
Expand Down
10 changes: 10 additions & 0 deletions tests/formatters/consoleFormatter.test.js.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ exports[`consoleFormatter > formatConsoleEventShort > formats a console.log mess
msgid=2 [log] Processing file: (1 args)
`;

exports[`consoleFormatter > formatConsoleEventVerbose > formats a console message with a stack trace 1`] = `
ID: 5
Message: log> Hello stack trace!
### Stack trace
at foo (foo.ts:10:2)
at bar (foo.ts:20:2)
--- setTimeout -------------------------
at schedule (util.ts:5:2)
`;

exports[`consoleFormatter > formatConsoleEventVerbose > formats a console.error message 1`] = `
ID: 4
Message: error> Something went wrong
Expand Down
43 changes: 43 additions & 0 deletions tests/formatters/consoleFormatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
formatConsoleEventShort,
formatConsoleEventVerbose,
} from '../../src/formatters/consoleFormatter.js';
import type {DevTools} from '../../src/third_party/index.js';
import {getMockAggregatedIssue} from '../utils.js';

describe('consoleFormatter', () => {
Expand Down Expand Up @@ -92,6 +93,48 @@ describe('consoleFormatter', () => {
const result = formatConsoleEventVerbose(message);
t.assert.snapshot?.(result);
});

it('formats a console message with a stack trace', t => {
const message: ConsoleMessageData = {
consoleMessageStableId: 5,
type: 'log',
message: 'Hello stack trace!',
args: [],
stackTrace: {
syncFragment: {
frames: [
{
line: 10,
column: 2,
url: 'foo.ts',
name: 'foo',
},
{
line: 20,
column: 2,
url: 'foo.ts',
name: 'bar',
},
],
},
asyncFragments: [
{
description: 'setTimeout',
frames: [
{
line: 5,
column: 2,
url: 'util.ts',
name: 'schedule',
},
],
},
],
} as unknown as DevTools.StackTrace.StackTrace.StackTrace,
};
const result = formatConsoleEventVerbose(message);
t.assert.snapshot?.(result);
});
});

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