-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
normalizepaths.ts
100 lines (81 loc) · 2.91 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
import { convertIntegrationFnToClass } from '@sentry/core';
import type { IntegrationFn } from '@sentry/types';
import { createStackParser, dirname, nodeStackLineParser } from '@sentry/utils';
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[][];
if (paths.length == 0) {
return undefined;
}
if (paths.length == 1) {
// Assume the single file is in the root
return dirname(paths[0].join('/'));
}
// Iterate over the paths and bail out when they no longer have a common root
let i = 0;
while (paths[0][i] && paths.every(w => w[i] === paths[0][i])) {
i++;
}
return paths[0].slice(0, i).join('/');
}
function getCwd(): string | 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: IntegrationFn = () => {
// Cached here
let appRoot: string | undefined;
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;
},
};
};
/** Normalises paths to the app root directory. */
// eslint-disable-next-line deprecation/deprecation
export const NormalizePaths = convertIntegrationFnToClass(INTEGRATION_NAME, normalizePathsIntegration);