1
- import type { Event , EventProcessor , Integration } from '@sentry/types' ;
1
+ import type { Event , EventProcessor , Integration , StackFrame } from '@sentry/types' ;
2
+ import { stripUrlQueryAndFragment } from '@sentry/utils' ;
3
+
4
+ import { WINDOW } from '../helpers' ;
2
5
3
6
interface ContextLinesOptions {
4
7
/**
@@ -29,11 +32,7 @@ export class ContextLines implements Integration {
29
32
*/
30
33
public name : string = ContextLines . id ;
31
34
32
- public constructor (
33
- private readonly _options : ContextLinesOptions = {
34
- frameContextLines : 0 ,
35
- } ,
36
- ) { }
35
+ public constructor ( private readonly _options : ContextLinesOptions = { } ) { }
37
36
38
37
/**
39
38
* @inheritDoc
@@ -44,6 +43,70 @@ export class ContextLines implements Integration {
44
43
45
44
/** Processes an event and adds context lines */
46
45
public addSourceContext ( event : Event ) : Event {
46
+ const doc = WINDOW . document ;
47
+ const htmlFilename = WINDOW . location && stripUrlQueryAndFragment ( WINDOW . location . href ) ;
48
+ if ( ! doc || ! htmlFilename ) {
49
+ return event ;
50
+ }
51
+
52
+ const exceptions = event . exception && event . exception . values ;
53
+ if ( ! exceptions || ! exceptions . length ) {
54
+ return event ;
55
+ }
56
+
57
+ const html = doc . documentElement . innerHTML ;
58
+
59
+ const htmlLines = [ '<!DOCTYPE html>' , '<html>' , ...html . split ( '\n' ) , '</html>' ] ;
60
+ if ( ! htmlLines . length ) {
61
+ return event ;
62
+ }
63
+
64
+ exceptions . forEach ( exception => {
65
+ const stacktrace = exception . stacktrace ;
66
+ if ( stacktrace && stacktrace . frames ) {
67
+ stacktrace . frames = stacktrace . frames . map ( frame =>
68
+ applySourceContextToFrame ( frame , htmlLines , htmlFilename , this . _options . frameContextLines || 7 ) ,
69
+ ) ;
70
+ }
71
+ } ) ;
72
+
47
73
return event ;
48
74
}
49
75
}
76
+
77
+ /**
78
+ * Only exported for testing
79
+ */
80
+ export function applySourceContextToFrame (
81
+ frame : StackFrame ,
82
+ htmlLines : string [ ] ,
83
+ htmlFilename : string ,
84
+ contextRange : number ,
85
+ ) : StackFrame {
86
+ if ( frame . filename !== htmlFilename || ! frame . lineno || ! htmlLines . length ) {
87
+ return frame ;
88
+ }
89
+
90
+ const sourroundingRange = Math . floor ( contextRange / 2 ) ;
91
+ const contextLineIndex = frame . lineno - 1 ;
92
+ const preStartIndex = Math . max ( contextLineIndex - sourroundingRange , 0 ) ;
93
+ const postEndIndex = Math . min ( contextLineIndex + sourroundingRange , htmlLines . length - 1 ) ;
94
+
95
+ const preLines = htmlLines . slice ( preStartIndex , contextLineIndex ) ;
96
+ const contextLine = htmlLines [ contextLineIndex ] ;
97
+ const postLines = htmlLines . slice ( contextLineIndex + 1 , postEndIndex + 1 ) ;
98
+
99
+ if ( preLines . length ) {
100
+ frame . pre_context = preLines ;
101
+ }
102
+
103
+ if ( contextLine ) {
104
+ frame . context_line = contextLine ;
105
+ }
106
+
107
+ if ( postLines . length ) {
108
+ frame . post_context = postLines || undefined ;
109
+ }
110
+
111
+ return frame ;
112
+ }
0 commit comments