Day34 - Eval Error Sources
Today I spent some time starting a PR for SCRIPT-8 to add jump to source support for the error messages. I didn't end up finishing the PR because I wasn't sure how the event routing should work, but I did write some regexes to pull out the source location and route the position data through to the error messages. The PR currently writes the position data to the log, but I am hopeful that with Gabriel's help it shouldn't be that hard to finish it up.
Why Not error-stack-parser
As far as I can tell, the recommended way for parsing error trace text is to use the error-stack-parser library. Unfortunately in our case, the source location we care about exists within an eval call. There is an open issue for error-stack-parser for this very case and as far as I can tell the package currently throws the data away.
The issue of parsing trace text is complicated by the fact that there isn't a standard format. The APIs grant access to the text, but do not say anything about what the text should be. As such each browser has a slightly different format. Luckily as far as I can tell, they all provide the data.
I did some further searching on the web, but never found anything to fix this, so I rolled up my sleeves and got out my regex references.
The first step was to gather example traces for each of the browser I have access to.
TypeError: Cannot read property 'toString' of undefined at print (print.js:7) at print (index.js:72) at draw (eval at <anonymous> (eval at evalCode (Iframe.js:644)), <anonymous>:2:3) at Iframe.drawUserGraphics (Iframe.js:397) at timerCallback (Iframe.js:702) at tick (interval.js:10) at timerFlush (timer.js:61) at wake (timer.js:71)
print@http://localhost:3001/static/js/bundle.js:69091:7 print@http://localhost:3001/static/js/bundle.js:68699:7 draw@http://localhost:3001/static/js/bundle.js line 67598 > eval line 6 > eval:2:3 drawUserGraphics@http://localhost:3001/static/js/bundle.js:67340:9 timerCallback@http://localhost:3001/static/js/bundle.js:67650:11 tick@http://localhost:3001/static/js/bundle.js:1475:5 timerFlush@http://localhost:3001/static/js/bundle.js:1580:40 wake@http://localhost:3001/static/js/bundle.js:1590:5
TypeError: Unable to get property 'toString' of undefined or null reference at print (http://localhost:3001/static/js/bundle.js:69091:3) at print (http://localhost:3001/static/js/bundle.js:68699:7) at draw (eval code:2:3) at drawUserGraphics (http://localhost:3001/static/js/bundle.js:67340:9) at timerCallback (http://localhost:3001/static/js/bundle.js:67650:11) at tick (http://localhost:3001/static/js/bundle.js:1475:5) at timerFlush (http://localhost:3001/static/js/bundle.js:1580:40) at wake (http://localhost:3001/static/js/bundle.js:1590:5)
From these logs, I was able to determine that the code position existed at the end of a single line in every case.
I hate writing Regular Expressions. I think they are clunky and lend themselves to a write only read never coding style. It turns out that most people hate them and are bad at writing them, but for some cases they are a necessary evil. When I am forced to dabble, I tend to turn toward a regex authoring tool such as regex101. I find the experience of writing regexes much less painful when I can see what gets recognized in real time and when the reference information is visible and handy.
Writing each of the browser trace regexes was as simple as copying the
associated log into the test string box, and copying the line with the eval
position into the regex field. This doesn't work as is because I need the code
to be more generalized, and there are special characters, so I added backslashes
before the parentheses, replaced the numbers with
\d+ to match one or more
digits, and replaced the file names with
.+ to match one or more characters.
Finally I surrounded the actual line and column
\d+-s with un-escaped
parenthesis in order to mark them for extraction. And that was it! The regexes I
landed on were as follows:
chrome: /\(eval at <anonymous> \(eval at evalCode \(.+:\d+\)\), <anonymous>:(\d+):(\d+)\)/ firefox: /line \d+ > eval line \d+ > eval:(\d+):(\d+)/ edge: /\(eval code:(\d+):(\d+)\)/
Pretty straight forward.
The actual guts of the code was mostly a bit of plumbing from the iframe to the outer window, and to surface the position data in an event when the error list item gets clicked. Nothing particularly interesting or confusing.
I pushed my code and created this PR. Moments ago as I was writing this, Gabriel told me he would be happy to help push the PR the rest of the way, so I mark today's work as a success!