1*26ad19fcSEvan Baconimport type { StackFrame } from 'stacktrace-parser';
2*26ad19fcSEvan Bacon
3*26ad19fcSEvan Baconexport type MetroStackFrame = StackFrame & { collapse?: boolean };
4*26ad19fcSEvan Bacon
5*26ad19fcSEvan Baconexport function formatProjectFilePath(projectRoot: string, file?: string | null): string {
6*26ad19fcSEvan Bacon  if (file == null) {
7*26ad19fcSEvan Bacon    return '<unknown>';
8*26ad19fcSEvan Bacon  }
9*26ad19fcSEvan Bacon
10*26ad19fcSEvan Bacon  return pathRelativeToPath(file.replace(/\\/g, '/'), projectRoot.replace(/\\/g, '/')).replace(
11*26ad19fcSEvan Bacon    /\?.*$/,
12*26ad19fcSEvan Bacon    ''
13*26ad19fcSEvan Bacon  );
14*26ad19fcSEvan Bacon}
15*26ad19fcSEvan Bacon
16*26ad19fcSEvan Baconfunction pathRelativeToPath(path: string, relativeTo: string, sep = '/') {
17*26ad19fcSEvan Bacon  const relativeToParts = relativeTo.split(sep);
18*26ad19fcSEvan Bacon  const pathParts = path.split(sep);
19*26ad19fcSEvan Bacon  let i = 0;
20*26ad19fcSEvan Bacon  while (i < relativeToParts.length && i < pathParts.length) {
21*26ad19fcSEvan Bacon    if (relativeToParts[i] !== pathParts[i]) {
22*26ad19fcSEvan Bacon      break;
23*26ad19fcSEvan Bacon    }
24*26ad19fcSEvan Bacon    i++;
25*26ad19fcSEvan Bacon  }
26*26ad19fcSEvan Bacon  return pathParts.slice(i).join(sep);
27*26ad19fcSEvan Bacon}
28*26ad19fcSEvan Bacon
29*26ad19fcSEvan Baconexport function getStackFormattedLocation(projectRoot: string, frame: MetroStackFrame) {
30*26ad19fcSEvan Bacon  const column = frame.column != null && parseInt(String(frame.column), 10);
31*26ad19fcSEvan Bacon  const location =
32*26ad19fcSEvan Bacon    formatProjectFilePath(projectRoot, frame.file) +
33*26ad19fcSEvan Bacon    (frame.lineNumber != null
34*26ad19fcSEvan Bacon      ? ':' + frame.lineNumber + (column && !isNaN(column) ? ':' + (column + 1) : '')
35*26ad19fcSEvan Bacon      : '');
36*26ad19fcSEvan Bacon
37*26ad19fcSEvan Bacon  return location;
38*26ad19fcSEvan Bacon}
39