All files / src/utils stacktrace.ts

98% Statements 98/100
93.02% Branches 40/43
100% Functions 1/1
98% Lines 98/100

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 1011x 1x 1x 1x 1x 1x 1x 1x 1x 1x 26x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 26x 26x 26x 73x 73x 28x 28x 28x 28x 2x 2x 26x 26x 28x     26x 26x 26x 26x 26x 28x 28x 28x 73x 45x 45x 11x 11x 11x 11x 1x 1x 10x 10x 11x 2x 2x 10x 10x 10x 11x 11x 11x 11x 11x 11x 11x 34x 34x 45x 10x 10x 10x 10x 1x 1x 9x 9x 10x 2x 2x 9x 9x 9x 9x 10x 10x 10x 10x 45x 73x 24x 24x 26x 1x 1x 23x 23x  
import type { StackFrame } from '../types';
 
export interface ParseStackOptions {
  captureNodeModules?: boolean;
  captureColumn?: boolean;
  relativePathOnly?: boolean;
  maxFrames?: number;
}
 
export function parseStackTrace(stack?: string, options: ParseStackOptions = {}): StackFrame[] {
  if (!stack) return [];
 
  const {
    captureNodeModules = false,
    captureColumn = true,
    relativePathOnly = false,
    maxFrames,
  } = options;
 
  const frames: StackFrame[] = [];
  const lines = stack.split('\n');
  const origin = typeof location !== 'undefined' ? `${location.protocol}//${location.host}` : '';
 
  for (const line of lines) {
    const match = line.match(/^\s*at\s*(.+?)\s*\((.+?):(\d+):(\d+)\)\s*$/);
    if (match) {
      const [, fn, filename, lineNum, colNum] = match;
      
      const isNodeModules = filename.includes('node_modules');
      if (!captureNodeModules && isNodeModules) {
        continue;
      }
 
      let finalFilename = filename;
      if (relativePathOnly && origin && filename.startsWith(origin)) {
        finalFilename = filename.substring(origin.length);
      }
 
      frames.push({
        filename: finalFilename,
        function: fn,
        lineno: parseInt(lineNum, 10),
        colno: captureColumn ? parseInt(colNum, 10) : undefined,
        in_app: !isNodeModules,
      });
    } else {
      const v8Match = line.match(/^\s*at\s*(.+?)@(.+?):(\d+):(\d+)\s*$/);
    if (v8Match) {
      const [, fn, filename, lineNum, colNum] = v8Match;
      
      const isNodeModules = filename.includes('node_modules');
      if (!captureNodeModules && isNodeModules) {
        continue;
      }
 
      let finalFilename = filename;
      if (relativePathOnly && origin && filename.startsWith(origin)) {
        finalFilename = filename.substring(origin.length);
      }
 
      frames.push({
        filename: finalFilename,
        function: fn || '<anonymous>',
        lineno: parseInt(lineNum, 10),
        colno: captureColumn ? parseInt(colNum, 10) : undefined,
        in_app: !isNodeModules,
      });
      continue;
    }
 
    const urlMatch = line.match(/^\s*at\s*(.+?):(\d+):(\d+)\s*$/);
    if (urlMatch) {
      const [, filename, lineNum, colNum] = urlMatch;
        
        const isNodeModules = filename.includes('node_modules');
        if (!captureNodeModules && isNodeModules) {
          continue;
        }
 
        let finalFilename = filename;
        if (relativePathOnly && origin && filename.startsWith(origin)) {
          finalFilename = filename.substring(origin.length);
        }
 
        frames.push({
          filename: finalFilename,
          lineno: parseInt(lineNum, 10),
          colno: captureColumn ? parseInt(colNum, 10) : undefined,
          in_app: !isNodeModules,
        });
      }
    }
  }
 
  const reversed = frames.reverse();
  if (maxFrames !== undefined && maxFrames > 0) {
    return reversed.slice(0, maxFrames);
  }
  return reversed;
}