tenebrous-dicebot/api/node_modules/pretty-error/src/ParsedError.coffee

226 lines
5.1 KiB
CoffeeScript

sysPath = require 'path'
module.exports = class ParsedError
constructor: (@error) ->
do @_parse
_parse: ->
@_trace = []
@_kind = 'Error'
@_wrapper = ''
@_wrapper = String @error.wrapper if @error.wrapper?
unless typeof @error is 'object'
@_message = String @error
else
@_stack = @error.stack
if @error.kind?
@_kind = String @error.kind
else if typeof @_stack is 'string'
if m = @_stack.match /^([a-zA-Z0-9\_\$]+):\ /
@_kind = m[1]
if typeof @_stack is 'string'
@_parseStack()
else
@_message = @error.message? and String(@error.message) or ''
return
_parseStack: ->
messageLines = []
reachedTrace = no
for line in @_stack.split '\n'
continue if line.trim() is ''
if reachedTrace
@_trace.push @_parseTraceItem line
else
if line.match /^\s*at\s.+/
reachedTrace = yes
@_trace.push @_parseTraceItem line
else
messageLines.push line
message = messageLines.join '\n'
if message.substr(0, @_kind.length) is @_kind
message =
message
.substr(@_kind.length, message.length)
.replace(/^\:\s+/, '')
@_message = message
return
_parseTraceItem: (text) ->
text = text.trim()
return if text is ''
return text unless text.match /^at\ /
# remove the 'at ' part
text = text.replace /^at /, ''
return if text in ['Error (<anonymous>)', 'Error (<anonymous>:null:null)']
original = text
# the part that comes before the address
what = null
# address, including path to module and line/col
addr = null
# path to module
path = null
# module dir
dir = null
# module basename
file = null
# line number (if using a compiler, the line number of the module
# in that compiler will be used)
line = null
# column, same as above
col = null
# if using a compiler, this will translate to the line number of
# the js equivalent of that module
jsLine = null
# like above
jsCol = null
# path that doesn't include `node_module` dirs
shortenedPath = null
# like above
shortenedAddr = null
packageName = '[current]'
# pick out the address
if m = text.match /\(([^\)]+)\)$/
addr = m[1].trim()
if addr?
what = text.substr 0, text.length - addr.length - 2
what = what.trim()
# might not have a 'what' clause
unless addr?
addr = text.trim()
addr = @_fixPath addr
remaining = addr
# remove the <js> clause if the file is a compiled one
if m = remaining.match /\,\ <js>:(\d+):(\d+)$/
jsLine = m[1]
jsCol = m[2]
remaining = remaining.substr 0, remaining.length - m[0].length
# the line/col part
if m = remaining.match /:(\d+):(\d+)$/
line = m[1]
col = m[2]
remaining = remaining.substr 0, remaining.length - m[0].length
path = remaining
# file and dir
if path?
file = sysPath.basename path
dir = sysPath.dirname path
if dir is '.' then dir = ''
path = @_fixPath path
file = @_fixPath file
dir = @_fixPath dir
if dir?
d = dir.replace /[\\]{1,2}/g, '/'
if m = d.match ///
node_modules/([^/]+)(?!.*node_modules.*)
///
packageName = m[1]
unless jsLine?
jsLine = line
jsCol = col
if path?
r = @_rectifyPath path
shortenedPath = r.path
shortenedAddr = shortenedPath + addr.substr(path.length, addr.length)
packages = r.packages
original: original
what: what
addr: addr
path: path
dir: dir
file: file
line: parseInt line
col: parseInt col
jsLine: parseInt jsLine
jsCol: parseInt jsCol
packageName: packageName
shortenedPath: shortenedPath
shortenedAddr: shortenedAddr
packages: packages || []
_getMessage: -> @_message
_getKind: -> @_kind
_getWrapper: -> @_wrapper
_getStack: -> @_stack
_getArguments: -> @error.arguments
_getType: -> @error.type
_getTrace: -> @_trace
_fixPath: (path) -> path.replace(///[\\]{1,2}///g, '/')
_rectifyPath: (path, nameForCurrentPackage) ->
path = String path
remaining = path
return path: path, packages: [] unless m = path.match /^(.+?)\/node_modules\/(.+)$/
parts = []
packages = []
if typeof nameForCurrentPackage is 'string'
parts.push "[#{nameForCurrentPackage}]"
packages.push "[#{nameForCurrentPackage}]"
else
parts.push "[#{m[1].match(/([^\/]+)$/)[1]}]"
packages.push m[1].match(/([^\/]+)$/)[1]
rest = m[2]
while m = rest.match /([^\/]+)\/node_modules\/(.+)$/
parts.push "[#{m[1]}]"
packages.push m[1]
rest = m[2]
if m = rest.match /([^\/]+)\/(.+)$/
parts.push "[#{m[1]}]"
packages.push m[1]
rest = m[2]
parts.push rest
path: parts.join "/"
packages: packages
for prop in ['message', 'kind', 'arguments', 'type', 'stack', 'trace', 'wrapper'] then do ->
methodName = '_get' + prop[0].toUpperCase() + prop.substr(1, prop.length)
Object.defineProperty ParsedError::, prop,
get: -> this[methodName]()