-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathnormalizepaths.ts
118 lines (97 loc) · 3.19 KB
/
normalizepaths.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import type { IntegrationFn } from '@sentry/core';
import { createStackParser, defineIntegration, dirname, nodeStackLineParser } from '@sentry/core';
const INTEGRATION_NAME = 'NormalizePaths';
function appRootFromErrorStack(error: Error): string | undefined {
// We know at the other end of the stack from here is the entry point that called 'init'
// We assume that this stacktrace will traverse the root of the app
const frames = createStackParser(nodeStackLineParser())(error.stack || '');
const paths = frames
// We're only interested in frames that are in_app with filenames
.filter(f => f.in_app && f.filename)
.map(
f =>
(f.filename as string)
.replace(/^[A-Z]:/, '') // remove Windows-style prefix
.replace(/\\/g, '/') // replace all `\` instances with `/`
.split('/')
.filter(seg => seg !== ''), // remove empty segments
) as string[][];
const firstPath = paths[0];
if (!firstPath) {
return undefined;
}
if (paths.length == 1) {
// Assume the single file is in the root
return dirname(firstPath.join('/'));
}
// Iterate over the paths and bail out when they no longer have a common root
let i = 0;
while (firstPath[i] && paths.every(w => w[i] === firstPath[i])) {
i++;
}
return firstPath.slice(0, i).join('/');
}
function getCwd(): string | undefined {
// Deno.permissions.querySync is not available on Deno Deploy
if (!Deno.permissions.querySync) {
return undefined;
}
// We don't want to prompt for permissions so we only get the cwd if
// permissions are already granted
const permission = Deno.permissions.querySync({ name: 'read', path: './' });
try {
if (permission.state == 'granted') {
return Deno.cwd();
}
} catch (_) {
//
}
return undefined;
}
const _normalizePathsIntegration = (() => {
// Cached here
let appRoot: string | undefined;
/** Get the app root, and cache it after it was first fetched. */
function getAppRoot(error: Error): string | undefined {
if (appRoot === undefined) {
appRoot = getCwd() || appRootFromErrorStack(error);
}
return appRoot;
}
return {
name: INTEGRATION_NAME,
processEvent(event) {
// This error.stack hopefully contains paths that traverse the app cwd
const error = new Error();
const appRoot = getAppRoot(error);
if (appRoot) {
for (const exception of event.exception?.values || []) {
for (const frame of exception.stacktrace?.frames || []) {
if (frame.filename && frame.in_app) {
const startIndex = frame.filename.indexOf(appRoot);
if (startIndex > -1) {
const endIndex = startIndex + appRoot.length;
frame.filename = `app://${frame.filename.substring(endIndex)}`;
}
}
}
}
}
return event;
},
};
}) satisfies IntegrationFn;
/**
* Normalises paths to the app root directory.
*
* Enabled by default in the Deno SDK.
*
* ```js
* Sentry.init({
* integrations: [
* Sentry.normalizePathsIntegration(),
* ],
* })
* ```
*/
export const normalizePathsIntegration = defineIntegration(_normalizePathsIntegration);