diff --git a/examples/index.js b/examples/index.js index f657276..7a3a4e3 100644 --- a/examples/index.js +++ b/examples/index.js @@ -17,7 +17,6 @@ function foo () { } http.createServer((req, res) => { - let youch = null try { foo () diff --git a/package.json b/package.json index da04bec..84bcbfd 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "mustache": "^2.3.0", - "stackman": "^1.2.7" + "stack-trace": "0.0.9" }, "repository": { "type": "git", diff --git a/src/Youch/index.js b/src/Youch/index.js index b8064e4..b8db29c 100644 --- a/src/Youch/index.js +++ b/src/Youch/index.js @@ -11,23 +11,47 @@ 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. @@ -35,10 +59,17 @@ class Youch { * @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) }) } @@ -77,7 +108,7 @@ class Youch { classes.push('active') } - if (!frame.isApp()) { + if (!this._isApp(frame)) { classes.push('native-frame') } @@ -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 @@ -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) : [] } } diff --git a/src/resources/error.mustache b/src/resources/error.mustache index 50634d4..36f2b4c 100644 --- a/src/resources/error.mustache +++ b/src/resources/error.mustache @@ -288,6 +288,120 @@ } + + @@ -492,34 +624,41 @@
-
+ -
- {{#frames}} - {{index}} -
-
- {{ file }}:{{ line }}:{{ column }} -
-
- {{ method }} -
-
{{ context.pre }} +
+
+ + +
+ +
+ {{#frames}} + {{index}} +
+
+ {{ file }}:{{ line }}:{{ column }} +
+
+ {{ method }} +
+
{{ context.pre }} {{ context.line }} {{ context.post }} +
-
- {{/frames}} + {{/frames}} +
@@ -585,6 +724,25 @@ Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|