|
1 |
| -import type { Event, IntegrationFn, StackFrame, Stacktrace } from '@sentry/types'; |
2 |
| -import { basename, relative } from '@sentry/utils'; |
| 1 | +import type { Event, StackFrame, Stacktrace } from '@sentry/types'; |
| 2 | +import { GLOBAL_OBJ, basename, relative } from '@sentry/utils'; |
3 | 3 | import { defineIntegration } from '../integration';
|
4 | 4 |
|
5 | 5 | type StackFrameIteratee = (frame: StackFrame) => StackFrame;
|
6 | 6 |
|
7 | 7 | const INTEGRATION_NAME = 'RewriteFrames';
|
8 | 8 |
|
9 | 9 | interface RewriteFramesOptions {
|
| 10 | + /** |
| 11 | + * Root path (the beginning of the path) that will be stripped from the frames' filename. |
| 12 | + * |
| 13 | + * This option has slightly different behaviour in the browser and on servers: |
| 14 | + * - In the browser, the value you provide in `root` will be stripped from the beginning stack frames' paths (if the path started with the value). |
| 15 | + * - On the server, the root value will only replace the beginning of stack frame filepaths, when the path is absolute. If no `root` value is provided and the path is absolute, the frame will be reduced to only the filename and the provided `prefix` option. |
| 16 | + * |
| 17 | + * Browser example: |
| 18 | + * - Original frame: `'http://example.com/my/path/static/asset.js'` |
| 19 | + * - `root: 'http://example.com/my/path'` |
| 20 | + * - `assetPrefix: 'app://'` |
| 21 | + * - Resulting frame: `'app:///static/asset.js'` |
| 22 | + * |
| 23 | + * Server example: |
| 24 | + * - Original frame: `'/User/local/my/path/static/asset.js'` |
| 25 | + * - `root: '/User/local/my/path'` |
| 26 | + * - `assetPrefix: 'app://'` |
| 27 | + * - Resulting frame: `'app:///static/asset.js'` |
| 28 | + */ |
10 | 29 | root?: string;
|
| 30 | + |
| 31 | + /** |
| 32 | + * A custom prefix that stack frames will be prepended with. |
| 33 | + * |
| 34 | + * Default: `'app://'` |
| 35 | + * |
| 36 | + * This option has slightly different behaviour in the browser and on servers: |
| 37 | + * - In the browser, the value you provide in `prefix` will prefix the resulting filename when the value you provided in `root` was applied. Effectively replacing whatever `root` matched in the beginning of the frame with `prefix`. |
| 38 | + * - On the server, the prefix is applied to all stackframes with absolute paths. On Windows, the drive identifier (e.g. "C://") is replaced with the prefix. |
| 39 | + */ |
11 | 40 | prefix?: string;
|
| 41 | + |
| 42 | + /** |
| 43 | + * Defines an iterator that is used to iterate through all of the stack frames for modification before being sent to Sentry. |
| 44 | + * Setting this option will effectively disable both the `root` and the `prefix` options. |
| 45 | + */ |
12 | 46 | iteratee?: StackFrameIteratee;
|
13 | 47 | }
|
14 | 48 |
|
15 |
| -const _rewriteFramesIntegration = ((options: RewriteFramesOptions = {}) => { |
| 49 | +/** |
| 50 | + * Rewrite event frames paths. |
| 51 | + */ |
| 52 | +export const rewriteFramesIntegration = defineIntegration((options: RewriteFramesOptions = {}) => { |
16 | 53 | const root = options.root;
|
17 | 54 | const prefix = options.prefix || 'app:///';
|
18 | 55 |
|
19 |
| - const iteratee: StackFrameIteratee = |
20 |
| - options.iteratee || |
21 |
| - ((frame: StackFrame) => { |
22 |
| - if (!frame.filename) { |
23 |
| - return frame; |
24 |
| - } |
25 |
| - // Determine if this is a Windows frame by checking for a Windows-style prefix such as `C:\` |
26 |
| - const isWindowsFrame = |
27 |
| - /^[a-zA-Z]:\\/.test(frame.filename) || |
28 |
| - // or the presence of a backslash without a forward slash (which are not allowed on Windows) |
29 |
| - (frame.filename.includes('\\') && !frame.filename.includes('/')); |
30 |
| - // Check if the frame filename begins with `/` |
31 |
| - const startsWithSlash = /^\//.test(frame.filename); |
32 |
| - if (isWindowsFrame || startsWithSlash) { |
33 |
| - const filename = isWindowsFrame |
34 |
| - ? frame.filename |
35 |
| - .replace(/^[a-zA-Z]:/, '') // remove Windows-style prefix |
36 |
| - .replace(/\\/g, '/') // replace all `\\` instances with `/` |
37 |
| - : frame.filename; |
38 |
| - const base = root ? relative(root, filename) : basename(filename); |
39 |
| - frame.filename = `${prefix}${base}`; |
40 |
| - } |
41 |
| - return frame; |
42 |
| - }); |
| 56 | + const isBrowser = 'window' in GLOBAL_OBJ && GLOBAL_OBJ.window !== undefined; |
| 57 | + |
| 58 | + const iteratee: StackFrameIteratee = options.iteratee || generateIteratee({ isBrowser, root, prefix }); |
43 | 59 |
|
44 | 60 | /** Process an exception event. */
|
45 | 61 | function _processExceptionsEvent(event: Event): Event {
|
@@ -81,9 +97,53 @@ const _rewriteFramesIntegration = ((options: RewriteFramesOptions = {}) => {
|
81 | 97 | return processedEvent;
|
82 | 98 | },
|
83 | 99 | };
|
84 |
| -}) satisfies IntegrationFn; |
| 100 | +}); |
85 | 101 |
|
86 | 102 | /**
|
87 |
| - * Rewrite event frames paths. |
| 103 | + * Exported only for tests. |
88 | 104 | */
|
89 |
| -export const rewriteFramesIntegration = defineIntegration(_rewriteFramesIntegration); |
| 105 | +export function generateIteratee({ |
| 106 | + isBrowser, |
| 107 | + root, |
| 108 | + prefix, |
| 109 | +}: { |
| 110 | + isBrowser: boolean; |
| 111 | + root?: string; |
| 112 | + prefix: string; |
| 113 | +}): StackFrameIteratee { |
| 114 | + return (frame: StackFrame) => { |
| 115 | + if (!frame.filename) { |
| 116 | + return frame; |
| 117 | + } |
| 118 | + |
| 119 | + // Determine if this is a Windows frame by checking for a Windows-style prefix such as `C:\` |
| 120 | + const isWindowsFrame = |
| 121 | + /^[a-zA-Z]:\\/.test(frame.filename) || |
| 122 | + // or the presence of a backslash without a forward slash (which are not allowed on Windows) |
| 123 | + (frame.filename.includes('\\') && !frame.filename.includes('/')); |
| 124 | + |
| 125 | + // Check if the frame filename begins with `/` |
| 126 | + const startsWithSlash = /^\//.test(frame.filename); |
| 127 | + |
| 128 | + if (isBrowser) { |
| 129 | + if (root) { |
| 130 | + const oldFilename = frame.filename; |
| 131 | + if (oldFilename.indexOf(root) === 0) { |
| 132 | + frame.filename = oldFilename.replace(root, prefix); |
| 133 | + } |
| 134 | + } |
| 135 | + } else { |
| 136 | + if (isWindowsFrame || startsWithSlash) { |
| 137 | + const filename = isWindowsFrame |
| 138 | + ? frame.filename |
| 139 | + .replace(/^[a-zA-Z]:/, '') // remove Windows-style prefix |
| 140 | + .replace(/\\/g, '/') // replace all `\\` instances with `/` |
| 141 | + : frame.filename; |
| 142 | + const base = root ? relative(root, filename) : basename(filename); |
| 143 | + frame.filename = `${prefix}${base}`; |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + return frame; |
| 148 | + }; |
| 149 | +} |
0 commit comments