22//
33// This should reduce a raw stack trace like this:
44//
5- // > foo.broken()@/src /foo.js
6- // > Bar@/src /bar.js
5+ // > foo.broken()@/example /foo.js
6+ // > Bar@/example /bar.js
77// > @/test/bar.test.js
88// > @/lib/qunit.js:500:12
99// > @/lib/qunit.js:100:28
1313//
1414// and shorten it to show up until the end of the user's bar.test.js code.
1515//
16- // > foo.broken()@/src /foo.js
17- // > Bar@/src /bar.js
16+ // > foo.broken()@/example /foo.js
17+ // > Bar@/example /bar.js
1818// > @/test/bar.test.js
1919//
2020// QUnit will obtain one example trace (once per process/pageload suffices),
3131//
3232// See also:
3333// - https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
34- //
35- const fileName = ( sourceFromStacktrace ( 0 ) || '' )
36- // Global replace, because a frame like localhost:4000/lib/qunit.js:1234:50,
37- // would otherwise (harmlessly, but uselessly) remove only the port (first match).
38- // https://github.com/qunitjs/qunit/issues/1769
39- . replace ( / ( : \d + ) + \) ? / g, '' )
40- // Remove anything prior to the last slash (Unix/Windows) from the last frame,
41- // leaving only "qunit.js".
42- . replace ( / .+ [ / \\ ] / , '' ) ;
34+
35+ function qunitFileName ( ) {
36+ let error = new Error ( ) ;
37+ if ( ! error . stack ) {
38+ // Copy of sourceFromStacktrace() to avoid circular dependency
39+ // Support: IE 9-11
40+ try {
41+ throw error ;
42+ } catch ( err ) {
43+ error = err ;
44+ }
45+ }
46+ return ( error . stack || '' )
47+ // Copy of extractStacktrace() to avoid circular dependency
48+ // Support: V8/Chrome
49+ . replace ( / ^ e r r o r $ \n / im, '' )
50+ . split ( '\n' ) [ 0 ]
51+ // Global replace, because a frame like localhost:4000/lib/qunit.js:1234:50,
52+ // would otherwise (harmlessly, but uselessly) remove only the port (first match).
53+ // https://github.com/qunitjs/qunit/issues/1769
54+ . replace ( / ( : \d + ) + \) ? / g, '' )
55+ // Remove anything prior to the last slash (Unix/Windows) from the last frame,
56+ // leaving only "qunit.js".
57+ . replace ( / .+ [ / \\ ] / , '' ) ;
58+ }
59+
60+ const fileName = qunitFileName ( ) ;
61+
62+ /**
63+ * Responsibilities:
64+ * - For internal errors from QUnit itself, remove the first qunit.js frames.
65+ * - For errors in Node.js, format any remaining qunit.js and node:internal
66+ * frames as internal (i.e. grey out).
67+ */
68+ export function annotateStacktrace ( e , formatInternal ) {
69+ if ( ! e || ! e . stack ) {
70+ return String ( e ) ;
71+ }
72+ const frames = e . stack . split ( '\n' ) ;
73+ const annotated = [ ] ;
74+ if ( e . toString ( ) . indexOf ( frames [ 0 ] ) !== - 1 ) {
75+ // In Firefox and Safari e.stack starts with frame 0, but in V8 (Chrome/Node.js),
76+ // e.stack starts first stringified message. Preserve this separately,
77+ // so that, below, we can distinguish between internal frames on top
78+ // (to remove) vs later internal frames (to format differently).
79+ annotated . push ( frames . shift ( ) ) ;
80+ }
81+ let initialInternal = true ;
82+ for ( let i = 0 ; i < frames . length ; i ++ ) {
83+ const frame = frames [ i ] ;
84+ const isInternal = ( ( fileName && frame . indexOf ( fileName ) !== - 1 ) || frame . indexOf ( 'node:internal/' ) !== - 1 ) ;
85+ if ( ! isInternal ) {
86+ initialInternal = false ;
87+ }
88+ // Remove initial internal frames entirely.
89+ if ( ! initialInternal ) {
90+ annotated . push ( isInternal ? formatInternal ( frame ) : frame ) ;
91+ }
92+ }
93+
94+ return annotated . join ( '\n' ) ;
95+ }
4396
4497export function extractStacktrace ( e , offset ) {
4598 offset = offset === undefined ? 4 : offset ;
4699
47100 // Support: IE9, e.stack is not supported, we will return undefined
48101 if ( e && e . stack ) {
49102 const stack = e . stack . split ( '\n' ) ;
103+ // In Firefox and Safari, e.stack starts immediately with the first frame.
104+ //
105+ // In V8 (Chrome/Node.js), the stack starts first with a stringified error message,
106+ // and the real stack starting on line 2.
50107 if ( / ^ e r r o r $ / i. test ( stack [ 0 ] ) ) {
51108 stack . shift ( ) ;
52109 }
@@ -69,8 +126,9 @@ export function extractStacktrace (e, offset) {
69126export function sourceFromStacktrace ( offset ) {
70127 let error = new Error ( ) ;
71128
72- // Support: Safari <=7 only, IE <=10 - 11 only
73- // Not all browsers generate the `stack` property for `new Error()`, see also #636
129+ // Support: IE 9-11, iOS 7
130+ // Not all browsers generate the `stack` property for `new Error()`
131+ // See also https://github.com/qunitjs/qunit/issues/636
74132 if ( ! error . stack ) {
75133 try {
76134 throw error ;
0 commit comments