Skip to content

Commit e154e66

Browse files
committed
fix: update stacktrace handler
1 parent d451d2e commit e154e66

File tree

2 files changed

+65
-46
lines changed

2 files changed

+65
-46
lines changed

src/stacktrace/parse.ts

+62-43
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@
88
*/
99

1010
import type { ParsedStack } from 'vitest'
11+
import path from 'node:path'
1112

1213
const stackIgnorePatterns = [
1314
'node:internal',
15+
/\/packages\/\w+\/dist\//,
16+
/\/@vitest\/\w+\/dist\//,
1417
'/vitest/dist/',
18+
'/vitest/src/',
19+
'/vite-node/dist/',
20+
'/vite-node/src/',
1521
'/node_modules/chai/',
1622
'/node_modules/tinypool/',
1723
'/node_modules/tinyspy/'
1824
]
1925

2026
const slash = (str: string) => str.replace(/\\/g, '/')
27+
const resolve = (str: string) => slash(path.resolve(str))
2128

2229
function notNullish<T>(v: T | null | undefined): v is NonNullable<T> {
2330
return v != null
@@ -33,59 +40,71 @@ function extractLocation(urlLike: string) {
3340
return [parts[1], parts[2] || undefined, parts[3] || undefined]
3441
}
3542

43+
// Based on https://github.com/stacktracejs/error-stack-parser
44+
// Credit to stacktracejs
45+
export function parseSingleStack(raw: string): ParsedStack | null {
46+
let line = raw.trim()
47+
48+
if (line.includes('(eval '))
49+
line = line
50+
.replace(/eval code/g, 'eval')
51+
.replace(/(\(eval at [^()]*)|(,.*$)/g, '')
52+
53+
let sanitizedLine = line
54+
.replace(/^\s+/, '')
55+
.replace(/\(eval code/g, '(')
56+
.replace(/^.*?\s+/, '')
57+
58+
// capture and preserve the parenthesized location "(/foo/my bar.js:12:87)" in
59+
// case it has spaces in it, as the string is split on \s+ later on
60+
const location = sanitizedLine.match(/ (\(.+\)$)/)
61+
62+
// remove the parenthesized location from the line, if it was matched
63+
sanitizedLine = location
64+
? sanitizedLine.replace(location[0], '')
65+
: sanitizedLine
66+
67+
// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
68+
// because this line doesn't have function name
69+
const [url, lineNumber, columnNumber] = extractLocation(
70+
location ? location[1]! : sanitizedLine
71+
)
72+
let method = (location && sanitizedLine) || ''
73+
let file = url && ['eval', '<anonymous>'].includes(url) ? undefined : url
74+
75+
if (!file || !lineNumber || !columnNumber) return null
76+
77+
if (method.startsWith('async ')) method = method.slice(6)
78+
79+
if (file.startsWith('file://')) file = file.slice(7)
80+
81+
// normalize Windows path (\ -> /)
82+
file = resolve(file)
83+
84+
return {
85+
method,
86+
file,
87+
line: parseInt(lineNumber),
88+
column: parseInt(columnNumber)
89+
}
90+
}
91+
3692
export const parseStacktrace = (
3793
stackStr: string,
3894
full = false
3995
): ParsedStack[] => {
4096
const stackFrames = stackStr
4197
.split('\n')
42-
// Based on https://github.com/stacktracejs/error-stack-parser
43-
// Credit to stacktracejs
4498
.map((raw): ParsedStack | null => {
45-
let line = raw.trim()
46-
47-
if (line.includes('(eval '))
48-
line = line
49-
.replace(/eval code/g, 'eval')
50-
.replace(/(\(eval at [^()]*)|(,.*$)/g, '')
51-
52-
let sanitizedLine = line
53-
.replace(/^\s+/, '')
54-
.replace(/\(eval code/g, '(')
55-
.replace(/^.*?\s+/, '')
56-
57-
// capture and preserve the parenthesized location "(/foo/my bar.js:12:87)" in
58-
// case it has spaces in it, as the string is split on \s+ later on
59-
const location = sanitizedLine.match(/ (\(.+\)$)/)
60-
61-
// remove the parenthesized location from the line, if it was matched
62-
sanitizedLine = location
63-
? sanitizedLine.replace(location[0]!, '')
64-
: sanitizedLine
65-
66-
// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
67-
// because this line doesn't have function name
68-
const [url, lineNumber, columnNumber] = extractLocation(
69-
location ? location[1]! : sanitizedLine
70-
)
71-
let method = (location && sanitizedLine) || ''
72-
let file = url && ['eval', '<anonymous>'].includes(url) ? undefined : url
73-
74-
if (!file || !lineNumber || !columnNumber) return null
99+
const stack = parseSingleStack(raw)
75100

76-
if (method.startsWith('async ')) method = method.slice(6)
77-
78-
if (file.startsWith('file://')) file = file.slice(7)
79-
80-
if (!full && stackIgnorePatterns.some(p => file && file.includes(p)))
101+
if (
102+
!stack ||
103+
(!full && stackIgnorePatterns.some(p => stack.file.match(p)))
104+
)
81105
return null
82106

83-
return {
84-
method,
85-
file: slash(file),
86-
line: parseInt(lineNumber),
87-
column: parseInt(columnNumber)
88-
}
107+
return stack
89108
})
90109
.filter(notNullish)
91110

test/__snapshots__/run.test.ts.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
// Vitest Snapshot v1
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

33
exports[`should output 1`] = `
44
[
55
"::group::Vitest Annotations",
6-
"::error title=AssertionError%3A expected 9 to be 10 // Object.is equality,file=test/fixtures/test/fail-test.test.ts,line=6,col=39:: at test/fixtures/test/fail-test.test.ts:6:39",
6+
"::error title=AssertionError%3A expected 9 to be 10 // Object.is equality,file=test/fixtures/test/fail-test.test.ts,line=6,col=17:: at test/fixtures/test/fail-test.test.ts:6:17",
77
"::error title=Error%3A error in file,file=test/fixtures/test/fail-file.test.ts,line=3,col=7:: at test/fixtures/test/fail-file.test.ts:3:7",
8-
"::error title=Error%3A foo,file=test/fixtures/src/throw.ts,line=2,col=9:: at Module.thrower test/fixtures/src/throw.ts:2:9%0A at test/fixtures/test/throw.test.ts:6:25",
8+
"::error title=Error%3A foo,file=test/fixtures/src/throw.ts,line=2,col=9:: at Module.thrower test/fixtures/src/throw.ts:2:9%0A at test/fixtures/test/throw.test.ts:5:3",
99
"::error title=Error%3A suite error,file=test/fixtures/test/fail-suit.test.ts,line=4,col=9:: at test/fixtures/test/fail-suit.test.ts:4:9",
1010
"::endgroup::",
1111
]

0 commit comments

Comments
 (0)