Class: SolidErrors::Frontend::StackParser

Inherits:
Object
  • Object
show all
Defined in:
lib/solid_errors/frontend/stack_parser.rb

Overview

Turns a browser stack trace string into structured frames.

Two dialects have to be handled. V8 (Chrome, Edge, node) prefixes every frame with "at" and puts the location in parentheses when a function name is known:

Error: boom
    at Object.connect (https://host/assets/x-abc123.js:42:15)
    at https://host/assets/x-abc123.js:1:1
    at new Foo (https://host/assets/x-abc123.js:7:9)
    at async load (https://host/assets/x-abc123.js:3:1)

SpiderMonkey/JavaScriptCore (Firefox, Safari) use "function@location" with no prefix, and an empty function name for top-level frames:

connect@https://host/assets/x-abc123.js:42:15
@https://host/assets/x-abc123.js:1:1

Defined Under Namespace

Classes: Frame

Constant Summary collapse

V8 =

"at fn (url:line:col)" / "at url:line:col". The location group is greedy up to the final two ":n" pairs so that URLs containing colons still split right.

/\A\s*at\s+(?:(?<function>.+?)\s+\()?(?<url>.+?):(?<line>\d+):(?<column>\d+)\)?\s*\z/
MOZ =

"fn@url:line:col"

/\A\s*(?<function>[^@]*)@(?<url>.+?):(?<line>\d+):(?<column>\d+)\s*\z/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stack) ⇒ StackParser

Returns a new instance of StackParser.



34
35
36
# File 'lib/solid_errors/frontend/stack_parser.rb', line 34

def initialize(stack)
  @stack = stack.to_s
end

Class Method Details

.parse(stack, limit: SolidErrors::Frontend.max_frames) ⇒ Object



30
31
32
# File 'lib/solid_errors/frontend/stack_parser.rb', line 30

def self.parse(stack, limit: SolidErrors::Frontend.max_frames)
  new(stack).parse(limit: limit)
end

Instance Method Details

#parse(limit: SolidErrors::Frontend.max_frames) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/solid_errors/frontend/stack_parser.rb', line 38

def parse(limit: SolidErrors::Frontend.max_frames)
  frames = []

  @stack.each_line do |raw_line|
    line = raw_line.strip
    next if line.empty?

    if (frame = frame_from(line))
      frames << frame
    elsif frames.any?
      # Something we can't decompose but that sits inside the trace, e.g.
      # Safari's "[native code]". Worth keeping verbatim.
      frames << Frame.new(raw: line)
    end
    # Anything unparseable *before* the first frame is the "Name: message"
    # header V8 prepends, which we already have as the message.

    break if frames.size >= limit
  end

  frames
end