-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstack-path.ts
More file actions
70 lines (65 loc) · 2.48 KB
/
Copy pathstack-path.ts
File metadata and controls
70 lines (65 loc) · 2.48 KB
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
import { dirname } from "node:path";
export function getCallerDirname(): string {
const e = new Error();
if (e.stack == null) {
Error.captureStackTrace(e);
}
return dirname(extractCallerPath(e.stack as string));
}
// Comprehensive regex patterns for different stack frame formats
const patterns =
process.platform === "win32"
? [
// File URLs: "at functionName (file:///C:/path/file.js:1:1)"
/\bat\s.+?\((?<path>file:\/\/\/.+?):\d+:\d+\)$/,
// File URLs direct: "at file:///C:/path/file.js:1:1"
/\bat\s(?<path>file:\/\/\/.+?):\d+:\d+$/,
// Standard: "at functionName (C:\path\file.js:1:1)"
/\bat\s.+?\((?<path>[A-Z]:\\.+):\d+:\d+\)$/,
// direct: "at C:\path\file.js:1:1"
/\bat\s(?<path>[A-Z]:\\.+):\d+:\d+$/,
// UNC: "at functionName (\\server\share\path\file.js:1:1)"
/\bat\s.+?\((?<path>\\\\.+):\d+:\d+\)$/,
// direct: "at \\server\share\path\file.js:1:1"
/\bat\s(?<path>\\\\.+):\d+:\d+$/,
]
: [
// Standard: "at functionName (/path/file.js:1:1)"
/\bat\s.+?\((?<path>\/.+?):\d+:\d+\)$/,
// Anonymous or direct: "at /path/file.js:1:1"
// eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers
/\bat\s(?:[^\s()]+\s)?(?<path>\/[^:]+):\d+:\d+$/,
];
const MaybeUrlRE = /^[a-z]{2,5}:\/\//i;
// only exposed for tests:
export function extractCallerPath(stack: string): string {
const frames = stack.split("\n").filter(Boolean);
// First find getCallerDirname() in the stack:
const callerFrame = frames.findIndex((frame) =>
frame.includes("getCallerDirname"),
);
if (callerFrame === -1) {
throw new Error("Invalid stack trace format: missing caller frame");
}
for (let i = callerFrame + 1; i < frames.length; i++) {
// eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop
const frame = frames[i];
for (const pattern of patterns) {
const g = frame?.trim().match(pattern)?.groups;
if (g != null && g["path"]) {
const path = g["path"];
// Windows requires us to check if it's a reasonable URL, as URL accepts
// "C:\\path\\file.txt" as valid (!!)
if (MaybeUrlRE.test(path)) {
try {
return new URL(path).pathname;
} catch {
// ignore
}
}
return path;
}
}
}
throw new Error("Invalid stack trace format: no parsable frames");
}