forked from SlimIO/pretty-stack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
137 lines (120 loc) · 3.87 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"use strict";
// Require Node.js Dependencies
const { basename, dirname, join } = require("path");
const { readFileSync } = require("fs");
// Require Third-party Dependencies
const cleanStack = require("clean-stack");
const { gray, white, cyan, yellow, bgRed, red } = require("kleur");
/**
* @function linesLength
* @param {!number} length
* @returns {number}
*/
function linesLength(length) {
if (length >= 0 && length <= 9) {
return 1;
}
else if (length >= 10 && length <= 99) {
return 2;
}
else if (length >= 100 && length <= 999) {
return 3;
}
return 4;
}
/**
* @function transformToArrayStack
* @param {*} obj
* @returns {string[]}
* @throws {Error}
*/
function transformToArrayStack(obj) {
if (obj instanceof Error) {
return cleanStack(obj.stack).split("\n");
}
else if (Array.isArray(obj)) {
return cleanStack(obj.join("\n")).split("\n");
}
else if (typeof obj === "string") {
return cleanStack(obj).split("\n");
}
throw new Error("Unsupported error arg type");
}
/**
* @function getPattern
* @param {!string} line
* @returns {any}
*/
function getPattern(line) {
const result = /at\s(.*)\s\((.*)\)/.exec(line);
if (result !== null) {
return result;
}
return /at\s(.*)/.exec(line);
}
/**
* @function prettyStack
* @param {!Error} error
* @param {boolean} [printFile=true]
* @returns string
*/
function prettyStack(error, printFile = true) {
const messageStack = [];
const arrStack = transformToArrayStack(error);
const mem = new Set();
let firstStack = null;
messageStack.push("\n " + bgRed(white().bold(` ${arrStack.shift()} `)) + "\n");
// console.log(arrStack);
for (const line of arrStack) {
const result = getPattern(line);
if (result === null) {
continue;
}
if (firstStack === null) {
firstStack = line;
}
let [, at, path] = result;
if (typeof path === "undefined") {
path = at;
at = null;
}
const [fileName, fileLine] = basename(path).split(":");
const fullName = join(dirname(path), fileName);
const linePosition = `${cyan().bold(fileName)} ${yellow().bold(`at line ${fileLine}`)}`;
const lineToLog = at === null ? linePosition : `at ${white().bold(at)} (${linePosition})`;
messageStack.push(gray().bold(` o ${lineToLog}`));
if (!mem.has(fullName)) {
messageStack.push(gray().bold(` ${path}\n`));
mem.add(fullName);
}
}
if (printFile && firstStack !== null) {
messageStack.push("");
const [, at, path = at] = getPattern(firstStack);
const [fileName, line, char] = basename(path).split(":");
const completePath = join(dirname(path), fileName);
try {
const lines = readFileSync(completePath, "utf-8").split("\n");
const len = linesLength(lines.length);
lines.forEach((value, index) => {
if (index >= line - 2 && index <= line) {
const isTheLine = index === line - 1;
const color = isTheLine ? white().bold : gray().bold;
const arrow = isTheLine ? cyan().bold(">") : " ";
const toAdd = len - `${index + 1}`.length;
messageStack.push(" " + gray().bold(`${arrow} ${index + 1}${" ".repeat(toAdd)} |`) + color(` ${value}`));
if (isTheLine) {
const sLen = " ".repeat(len);
messageStack.push(" " + sLen + gray().bold("| ") + " ".repeat(char - 1) + cyan().bold("^"));
}
}
});
}
catch (error) {
// Ignore
}
messageStack.push("");
}
return messageStack.join("\n");
}
module.exports = prettyStack;