Skip to content

Commit

Permalink
refactor(*): cleanup code and improve formatting
Browse files Browse the repository at this point in the history
got rid of stackman since it makes use of Error.prepareErrorStack which is buggy in itself
  • Loading branch information
thetutlage committed Jan 27, 2017
1 parent e421b2d commit d59d6c1
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 48 deletions.
1 change: 0 additions & 1 deletion examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ function foo () {
}

http.createServer((req, res) => {

let youch = null
try {
foo ()
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"dependencies": {
"mustache": "^2.3.0",
"stackman": "^1.2.7"
"stack-trace": "0.0.9"
},
"repository": {
"type": "git",
Expand Down
89 changes: 76 additions & 13 deletions src/Youch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,65 @@

const Mustache = require('mustache')
const path = require('path')
const stackTrace = require('stack-trace')
const fs = require('fs')
const VIEW_PATH = '../resources/error.mustache'
const startingSlashRegex = new RegExp(`^${path.sep}`)

const viewTemplate = fs.readFileSync(path.join(__dirname, VIEW_PATH), 'utf-8')

class Youch {
constructor (error, request) {
const options = {
context: 5
}

this.codeContext = 5
this._filterHeaders = ['cookie', 'connection']
this.stackman = require('stackman')(options)
this.error = error
this.request = request
}

/**
* Returns the source code for a given file. It unable to
* read file it resolves the promise with a null.
*
* @param {Object} frame
* @return {Promise}
*/
_getFrameSource (frame) {
return new Promise((resolve, reject) => {
fs.readFile(frame.getFileName(), 'utf-8', (error, contents) => {
if (error) {
resolve(null)
}

const lines = contents.split(/\r?\n/)
const lineNumber = frame.getLineNumber()

resolve({
pre: lines.slice(Math.max(0, lineNumber - (this.codeContext + 1)), lineNumber - 1),
line: lines[lineNumber - 1],
post: lines.slice(lineNumber, lineNumber + this.codeContext)
})
})
})
}

/**
* Parses the error stack and returns serialized
* frames out of it.
*
* @return {Object}
*/
_parseError () {
return new Promise((resolve) => {
this.stackman(this.error, (stack) => {
resolve(stack)
})
return new Promise((resolve, reject) => {
const stack = stackTrace.parse(this.error)
Promise.all(stack.map((frame) => {
if (this._isNode(frame)) {
return Promise.resolve(frame)
}
return this._getFrameSource(frame).then((context) => {
frame.context = context
return frame
})
})).then(resolve).catch(reject)
})
}

Expand Down Expand Up @@ -77,7 +108,7 @@ class Youch {
classes.push('active')
}

if (!frame.isApp()) {
if (!this._isApp(frame)) {
classes.push('native-frame')
}

Expand All @@ -104,15 +135,47 @@ class Youch {
* @return {Object}
*/
_serializeFrame (frame) {
const relativeFileName = frame.getFileName().indexOf(process.cwd()) > -1 ?
frame.getFileName().replace(process.cwd(), '').replace(startingSlashRegex, '') :
frame.getFileName()

return {
file: frame.getRelativeFileName(),
method: frame.getFunctionNameSanitized(),
file: relativeFileName,
method: frame.getFunctionName(),
line: frame.getLineNumber(),
column: frame.getColumnNumber(),
context: this._getContext(frame)
}
}

/**
* Returns whether frame belongs to nodejs
* or not.
*
* @return {Boolean} [description]
*/
_isNode (frame) {
if (frame.isNative()) {
return true
}

const filename = frame.getFileName() || ''
return !path.isAbsolute(filename) && filename[0] !== '.'
}

/**
* Returns whether code belongs to the app
* or not.
*
* @return {Boolean} [description]
*/
_isApp (frame) {
if (this._isNode(frame)) {
return false
}
return !~(frame.getFileName() || '').indexOf('node_modules' + path.sep)
}

/**
* Serializes stack to Mustache friendly object to
* be used within the view. Optionally can pass
Expand All @@ -129,7 +192,7 @@ class Youch {
message: this.error.message,
name: this.error.name,
status: this.error.status,
frames: stack.frames instanceof Array === true ? stack.frames.map(callback).filter((frame) => frame.file) : []
frames: stack instanceof Array === true ? stack.filter((frame) => frame.getFileName()).map(callback) : []
}
}

Expand Down
Loading

0 comments on commit d59d6c1

Please sign in to comment.