backtrace-node
Version:
Backtrace error reporting tool
245 lines • 10.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var crypto_1 = require("crypto");
var fs = tslib_1.__importStar(require("fs"));
var path = tslib_1.__importStar(require("path"));
var source_scan_1 = require("source-scan");
/**
* Analyse stack trace generated by exception
* Create Stack Frames and find calling library/program informaiton
*/
var BacktraceStackTrace = /** @class */ (function () {
function BacktraceStackTrace(error) {
this.error = error;
this.fault = true;
this.name = 'main';
this.stack = [];
this.sourceCodeInformation = {};
this.symbolicationPaths = new Set();
this.callingModulePath = '';
this.stackLineRe = /\s+at (.+) \((.+):(\d+):(\d+)\)/;
this.requestedSourceCode = {};
this.tabWidth = 8;
this.contextLineCount = 200;
}
BacktraceStackTrace.prototype.setSourceCodeOptions = function (tabWidth, contextLineCount) {
this.tabWidth = tabWidth;
this.contextLineCount = contextLineCount;
};
/**
* Get calling module path
*/
BacktraceStackTrace.prototype.getCallingModulePath = function () {
if (!this.stack || this.stack.length === 0) {
return undefined;
}
// handle a situation when every one stack frame is from node_modules
if (!this.callingModulePath) {
var sourceCode = this.stack.find(function (n) { return !!n.sourceCode; });
if (!sourceCode) {
return undefined;
}
for (var key in this.requestedSourceCode) {
if (this.requestedSourceCode.hasOwnProperty(key)) {
if (this.requestedSourceCode[key].id === sourceCode.sourceCode) {
this.callingModulePath = key;
}
}
}
}
return this.callingModulePath;
};
/**
* Get Json data from Stack trace object
*/
BacktraceStackTrace.prototype.toJson = function () {
return {
name: this.name,
fault: this.fault,
stack: this.stack,
};
};
/**
* Get source code information
*/
BacktraceStackTrace.prototype.getSourceCode = function () {
return this.sourceCodeInformation;
};
/**
* Start parsing stack frames
*/
BacktraceStackTrace.prototype.parseStackFrames = function (includeSymbolication) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var stackTrace, appPath, lines, backtracePath;
var _this = this;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
stackTrace = this.error.stack;
if (!stackTrace) {
return [2 /*return*/];
}
appPath = process.cwd();
lines = stackTrace.split('\n').slice(1);
backtracePath = path.join('node_modules', 'backtrace-node');
lines.forEach(function (line) {
var match = line.match(_this.stackLineRe);
if (!match || match.length < 4) {
return;
}
var fullSourceCodePath = match[2];
var backtraceLibStackFrame = fullSourceCodePath.indexOf(backtracePath) !== -1;
if (backtraceLibStackFrame) {
return;
}
var sourcePath = fullSourceCodePath;
if (sourcePath) {
sourcePath = path.relative(appPath, sourcePath);
}
var stackFrame = {
funcName: match[1],
path: sourcePath,
line: parseInt(match[3], 10),
column: parseInt(match[4], 10),
};
// ignore not existing stack frames
if (fs.existsSync(fullSourceCodePath)) {
stackFrame.sourceCode = _this.addSourceRequest(stackFrame, fullSourceCodePath);
// extend root object with symbolication information
if (includeSymbolication) {
_this.symbolicationPaths.add(fullSourceCodePath);
}
if (_this.isCallingModule(fullSourceCodePath)) {
_this.callingModulePath = fullSourceCodePath;
}
}
_this.stack.push(stackFrame);
});
// read source code information after reading all existing stack frames from stack trace
return [4 /*yield*/, this.readSourceCode()];
case 1:
// read source code information after reading all existing stack frames from stack trace
_a.sent();
if (includeSymbolication) {
this.generateSymbolicationMap();
}
return [2 /*return*/];
}
});
});
};
BacktraceStackTrace.prototype.generateSymbolicationMap = function () {
var _this = this;
if (this.symbolicationPaths.size === 0) {
return;
}
this.symbolicationMaps = [];
this.symbolicationPaths.forEach(function (symbolicationPath) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var file, hash;
var _a;
return tslib_1.__generator(this, function (_b) {
file = fs.readFileSync(symbolicationPath, 'utf8');
hash = crypto_1.createHash('md5').update(file).digest('hex');
(_a = this.symbolicationMaps) === null || _a === void 0 ? void 0 : _a.push({
file: symbolicationPath,
uuid: this.convertHexToUuid(hash),
});
return [2 /*return*/];
});
}); });
};
BacktraceStackTrace.prototype.convertHexToUuid = function (hex) {
return (hex.slice(0, 8) +
'-' +
hex.slice(8, 12) +
'-' +
hex.slice(12, 16) +
'-' +
hex.slice(16, 20) +
'-' +
hex.slice(20, 32));
};
BacktraceStackTrace.prototype.addSourceRequest = function (stackFrame, fullPath) {
// add source code to existing list. Otherwise create empty array
if (!this.requestedSourceCode[fullPath]) {
this.requestedSourceCode[fullPath] = {
data: [],
id: crypto_1.randomBytes(20).toString('hex'),
};
}
this.requestedSourceCode[fullPath].data.push({
line: stackFrame.line,
column: stackFrame.column,
});
return this.requestedSourceCode[fullPath].id;
};
BacktraceStackTrace.prototype.readSourceCode = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, _b, _i, key, element, minLine, maxLine, i, item, parameter, res;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = [];
for (_b in this.requestedSourceCode)
_a.push(_b);
_i = 0;
_c.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
key = _a[_i];
if (!this.requestedSourceCode.hasOwnProperty(key)) return [3 /*break*/, 3];
element = this.requestedSourceCode[key];
minLine = element.data[0].line;
maxLine = minLine;
for (i = 1; i < element.data.length; i += 1) {
item = element.data[i];
minLine = Math.min(minLine, item.line);
maxLine = Math.max(maxLine, item.line);
}
parameter = {
startLine: Math.max(minLine - this.contextLineCount + 1, 0),
endLine: maxLine + this.contextLineCount,
filePath: key,
};
return [4 /*yield*/, this.getSourceCodeInformation(parameter)];
case 2:
res = _c.sent();
this.sourceCodeInformation[element.id] = res;
_c.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/];
}
});
});
};
BacktraceStackTrace.prototype.getSourceCodeInformation = function (parameter) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _this = this;
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, new Promise(function (res, rej) {
source_scan_1.scanFile(parameter, function (err, buff) {
if (err) {
rej(err);
return;
}
res({
startLine: parameter.startLine + 1,
startColumn: 1,
text: buff.toString('utf8'),
tabWidth: _this.tabWidth,
});
});
})];
});
});
};
BacktraceStackTrace.prototype.isCallingModule = function (sourcePath) {
return !!sourcePath && !this.callingModulePath && !sourcePath.includes('node_modules');
};
return BacktraceStackTrace;
}());
exports.BacktraceStackTrace = BacktraceStackTrace;
//# sourceMappingURL=backtraceStackTrace.js.map