Skip to content

Commit e1ab1a2

Browse files
committed
fix: improve shortUrl to handle pptr: and encoded URLs
- Decode percent-encoded URLs before extracting basename so that pptr:evaluate;file%3A%2F%2F... resolves to the inner filename. - Recursively unwrap parenthesised inner URLs for the pptr:evaluateHandle;performEvaluation (file://...) form. - Fix timeout values: 5s for concise, 10s for detailed (was both 10s). - Use shortUrl for uiSourceCode paths too, falling back to displayName.
1 parent 8024d24 commit e1ab1a2

2 files changed

Lines changed: 45 additions & 12 deletions

File tree

src/formatters/ConsoleFormatter.ts

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,10 @@ function extractLocationFromStack(
522522
}
523523
if (frame.uiSourceCode) {
524524
const uiLocation = frame.uiSourceCode.uiLocation(frame.line, frame.column);
525+
const url = uiLocation.uiSourceCode.url();
525526
return {
526-
url: uiLocation.uiSourceCode.url(),
527-
displayName: uiLocation.uiSourceCode.displayName(),
527+
url,
528+
displayName: shortUrl(url) || uiLocation.uiSourceCode.displayName(),
528529
// UILocation stores 0-based line/column indices.
529530
lineNumber: uiLocation.lineNumber + 1,
530531
columnNumber: (uiLocation.columnNumber ?? 0) + 1,
@@ -593,7 +594,7 @@ async function withResolveTimeout<T>(
593594
promise: Promise<T>,
594595
fetchDetailedData: boolean | undefined,
595596
): Promise<T | undefined> {
596-
const timeoutMs = fetchDetailedData ? 10_000 : 10_000;
597+
const timeoutMs = fetchDetailedData ? 10_000 : 5_000;
597598
let timeoutId: ReturnType<typeof setTimeout> | undefined;
598599
const timeoutPromise = new Promise<undefined>(resolve => {
599600
timeoutId = setTimeout(() => resolve(undefined), timeoutMs);
@@ -610,21 +611,53 @@ async function withResolveTimeout<T>(
610611
/**
611612
* Extracts a short, human-readable name from a URL by returning the last path
612613
* segment. Used as a fallback when no source-mapped UI location is available.
614+
*
615+
* Handles a few special cases that come up in practice:
616+
* - puppeteer-injected scripts use a URL of the form
617+
* `pptr:evaluateHandle;performEvaluation (file:///path/to/file.js:104:34)`
618+
* or the URL-encoded variant
619+
* `pptr:evaluate;file%3A%2F%2F%2Fpath%2Fto%2Ffile.js%3A104%3A18`,
620+
* from which we extract the inner file's basename.
621+
* - URLs that already have a trailing `:line:column` suffix are stripped
622+
* before extracting the basename.
613623
*/
614624
function shortUrl(url: string): string {
615625
if (!url) {
616626
return '';
617627
}
628+
// Decode percent-encoded characters so embedded paths are extractable.
629+
let decoded = url;
618630
try {
619-
const parsed = new URL(url);
620-
const segments = parsed.pathname.split('/').filter(s => s.length > 0);
621-
if (segments.length > 0) {
622-
return segments[segments.length - 1];
631+
decoded = decodeURIComponent(url);
632+
} catch {
633+
// Leave url as-is when it is not validly percent-encoded.
634+
}
635+
// Recursively unwrap parenthesised inner URLs (puppeteer-style).
636+
const parenMatch = decoded.match(/\(([^()]+)\)/);
637+
if (parenMatch) {
638+
const inner = shortUrl(parenMatch[1]);
639+
if (inner) {
640+
return inner;
641+
}
642+
}
643+
// Strip trailing :line:col or :line so URL parsing succeeds.
644+
const cleaned = decoded.replace(/:\d+(?::\d+)?$/, '');
645+
try {
646+
const parsed = new URL(cleaned);
647+
const pathSegments = parsed.pathname.split('/').filter(s => s.length > 0);
648+
if (pathSegments.length > 0) {
649+
return pathSegments[pathSegments.length - 1];
650+
}
651+
// Some custom schemes (e.g. `pptr:`) leave the path in the opaque part.
652+
// Fall back to the last `/`-separated segment of the trimmed URL.
653+
const lastSlash = cleaned.lastIndexOf('/');
654+
if (lastSlash >= 0 && lastSlash < cleaned.length - 1) {
655+
return cleaned.slice(lastSlash + 1);
623656
}
624-
return parsed.host || url;
657+
return parsed.host || cleaned;
625658
} catch {
626659
// Not a valid URL (e.g. VM-backed inline scripts).
627-
const lastSlash = url.lastIndexOf('/');
628-
return lastSlash >= 0 ? url.slice(lastSlash + 1) : url;
660+
const lastSlash = cleaned.lastIndexOf('/');
661+
return lastSlash >= 0 ? cleaned.slice(lastSlash + 1) : cleaned;
629662
}
630663
}

tests/McpResponse.test.js.snapshot

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ exports[`McpResponse > adds color scheme emulation setting when it is set 2`] =
184184
exports[`McpResponse > adds console messages when the setting is true 1`] = `
185185
## Console messages
186186
Showing 1-1 of 1 (Page 1 of 1).
187-
msgid=1 [log] Hello from the test (1 args) pptr:evaluate;file%3A%2F%2F%2FUsers%2Fmasahiro.kokubo%2FDocuments%2FGitHub%2Fchrome-devtools-mcp%2F…:2:25
187+
msgid=1 [log] Hello from the test (1 args) McpResponse.test.js:2:25
188188
`;
189189

190190
exports[`McpResponse > adds console messages when the setting is true 2`] = `
@@ -206,7 +206,7 @@ exports[`McpResponse > adds console messages when the setting is true 2`] = `
206206
"id": 1,
207207
"location": {
208208
"url": "pptr:evaluate;<file-path>",
209-
"displayName": "pptr:evaluate;<file-path>",
209+
"displayName": "McpResponse.test.js",
210210
"lineNumber": 2,
211211
"columnNumber": 25
212212
}

0 commit comments

Comments
 (0)