Skip to content

Commit

Permalink
fix: update stacktrace handler
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Feb 26, 2023
1 parent d451d2e commit e154e66
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 46 deletions.
105 changes: 62 additions & 43 deletions src/stacktrace/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@
*/

import type { ParsedStack } from 'vitest'
import path from 'node:path'

const stackIgnorePatterns = [
'node:internal',
/\/packages\/\w+\/dist\//,
/\/@vitest\/\w+\/dist\//,
'/vitest/dist/',
'/vitest/src/',
'/vite-node/dist/',
'/vite-node/src/',
'/node_modules/chai/',
'/node_modules/tinypool/',
'/node_modules/tinyspy/'
]

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

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

// Based on https://github.com/stacktracejs/error-stack-parser
// Credit to stacktracejs
export function parseSingleStack(raw: string): ParsedStack | null {
let line = raw.trim()

if (line.includes('(eval '))
line = line
.replace(/eval code/g, 'eval')
.replace(/(\(eval at [^()]*)|(,.*$)/g, '')

let sanitizedLine = line
.replace(/^\s+/, '')
.replace(/\(eval code/g, '(')
.replace(/^.*?\s+/, '')

// capture and preserve the parenthesized location "(/foo/my bar.js:12:87)" in
// case it has spaces in it, as the string is split on \s+ later on
const location = sanitizedLine.match(/ (\(.+\)$)/)

// remove the parenthesized location from the line, if it was matched
sanitizedLine = location
? sanitizedLine.replace(location[0], '')
: sanitizedLine

// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
// because this line doesn't have function name
const [url, lineNumber, columnNumber] = extractLocation(
location ? location[1]! : sanitizedLine
)
let method = (location && sanitizedLine) || ''
let file = url && ['eval', '<anonymous>'].includes(url) ? undefined : url

if (!file || !lineNumber || !columnNumber) return null

if (method.startsWith('async ')) method = method.slice(6)

if (file.startsWith('file://')) file = file.slice(7)

// normalize Windows path (\ -> /)
file = resolve(file)

return {
method,
file,
line: parseInt(lineNumber),
column: parseInt(columnNumber)
}
}

export const parseStacktrace = (
stackStr: string,
full = false
): ParsedStack[] => {
const stackFrames = stackStr
.split('\n')
// Based on https://github.com/stacktracejs/error-stack-parser
// Credit to stacktracejs
.map((raw): ParsedStack | null => {
let line = raw.trim()

if (line.includes('(eval '))
line = line
.replace(/eval code/g, 'eval')
.replace(/(\(eval at [^()]*)|(,.*$)/g, '')

let sanitizedLine = line
.replace(/^\s+/, '')
.replace(/\(eval code/g, '(')
.replace(/^.*?\s+/, '')

// capture and preserve the parenthesized location "(/foo/my bar.js:12:87)" in
// case it has spaces in it, as the string is split on \s+ later on
const location = sanitizedLine.match(/ (\(.+\)$)/)

// remove the parenthesized location from the line, if it was matched
sanitizedLine = location
? sanitizedLine.replace(location[0]!, '')
: sanitizedLine

// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
// because this line doesn't have function name
const [url, lineNumber, columnNumber] = extractLocation(
location ? location[1]! : sanitizedLine
)
let method = (location && sanitizedLine) || ''
let file = url && ['eval', '<anonymous>'].includes(url) ? undefined : url

if (!file || !lineNumber || !columnNumber) return null
const stack = parseSingleStack(raw)

if (method.startsWith('async ')) method = method.slice(6)

if (file.startsWith('file://')) file = file.slice(7)

if (!full && stackIgnorePatterns.some(p => file && file.includes(p)))
if (
!stack ||
(!full && stackIgnorePatterns.some(p => stack.file.match(p)))
)
return null

return {
method,
file: slash(file),
line: parseInt(lineNumber),
column: parseInt(columnNumber)
}
return stack
})
.filter(notNullish)

Expand Down
6 changes: 3 additions & 3 deletions test/__snapshots__/run.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Vitest Snapshot v1
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`should output 1`] = `
[
"::group::Vitest Annotations",
"::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",
"::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",
"::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",
"::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",
"::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",
"::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",
"::endgroup::",
]
Expand Down

0 comments on commit e154e66

Please sign in to comment.