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 ()', 'Error (: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 clause if the file is a compiled one if m = remaining.match /\,\ :(\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]()