forked from alessioalex/dev-error-handler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
104 lines (85 loc) · 3.5 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* eslint-disable no-console, func-names */
'use strict';
var _ = require('lodash');
var fs = require('fs');
var stackTrace = require('stack-trace');
var mapAsync = require('tiny-map-async');
var errTo = require('errto');
var PrettyError = require('pretty-error');
var ejs = require('ejs');
var sep = require('path').sep;
var hljs = require('highlight.js');
var notifier = require('node-notifier');
if (process.env.NODE_ENV === 'production') {
throw new Error('dev-error-handler shouldn\'t be used in production');
}
hljs.configure({ lineNodes: true });
var baseStyle = fs.readFileSync(__dirname + '/public/css/base-style.css', 'utf8');
var hljsStyle = fs.readFileSync(__dirname + '/public/css/hljs-molokai-sublime.css', 'utf8');
var errorTmpl = fs.readFileSync(__dirname + '/public/template.html', 'utf8');
errorTmpl = errorTmpl.replace('<%- hljsStyle %>', hljsStyle);
errorTmpl = errorTmpl.replace('<%- baseStyle %>', baseStyle);
var render = ejs.compile(errorTmpl);
/* eslint-disable no-unused-vars */
module.exports = function(err, req, res, next) {
if (_.isNil(err.stack)) err.stack = "";
/* eslint-enable no-unused-vars */
var stack = stackTrace.parse(err);
// exclude native stuff, for ex:
// at Array.forEach (native)
stack = stack.filter(function(line) { return !line.native; });
mapAsync(stack, function getContentInfo(line, cb) {
if (_.isNil(line.fileName)) {
cb();
return;
}
var fileName = line.fileName;
var isNotNodeCore = (fileName.indexOf('internal' + sep) === -1) && (fileName.indexOf(sep) !== -1);
var isNotModule = !/node_modules/.test(fileName);
// exclude core node modules and node modules
if (isNotNodeCore && isNotModule) {
fs.readFile(line.fileName, 'utf-8', errTo(cb, function(data) {
// replace \r\n with => \n for Windows compat and strip the \n from the end of the file
// TODO: fix this inside hljs line code
var content = hljs.getHighlighted(data.replace(/\r\n/g, '\n').replace(/\n$/, ''), 'javascript').innerHTML;
var start = line.lineNumber - 5;
if (start < 0) { start = 0; }
var end = line.lineNumber + 4;
var snippet = content.split('\n').slice(start, end);
var errIndex = (snippet.length < 9) ? (line.lineNumber - start - 1) : (snippet.length - 5);
// decorate the error line
snippet[errIndex] = snippet[errIndex].replace('class="line"', 'class="error-line line"');
line.startAt = start;
line.content = snippet.join('\n');
cb(null, line);
}));
} else {
cb();
}
}, function(e, lns) {
if (e) {
console.error('something went bad when generating the detailed stack trace');
console.error('please open a github issue for dev-error-handler with the following data: ');
console.error('Error message: ' + e.message);
console.error('Parsed stack: \n' + err.stack);
}
var lines = lns || [];
// remove empty data from the array (coming from the excluded lines)
lines = lines.filter(function(line) { return !!line; });
// pretty print in the terminal
console.error((new PrettyError).render(err) || err.stack);
// bypass for e2e testing
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
// send OS notification
notifier.notify({
title: 'Error in ' + req.method + ' ' + req.url,
message: err.message
});
}
res.writeHead(500, { 'Content-Type': 'text/html' });
res.end(render({
err: err,
lines: lines
}));
});
};