-
Notifications
You must be signed in to change notification settings - Fork 1
/
resolver.js
131 lines (114 loc) · 3.92 KB
/
resolver.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
'use strict';
let SourceMap = require('source-map');
let SourceMapConsumer = SourceMap.SourceMapConsumer;
let Promise = require('bluebird');
let fs = Promise.promisifyAll(require('fs'));
let sourceMappingURL = require("source-map-url");
let sourceMapCache = {};
let ErrorStackParser = require('error-stack-parser');
let path = require('path');
function readSourceMap(mapFile) {
sourceMapCache[mapFile] = sourceMapCache[mapFile] || fs.readFileAsync(mapFile, 'utf8').then(JSON.parse).then(contents => {
return new SourceMapConsumer(contents);
}).then(function(smc) {
return smc;
});
return sourceMapCache[mapFile];
}
function extractDataUrl(dataUrl) {
return new Buffer(dataUrl.split(",")[1], 'base64').toString('utf8');
}
function resolveSourceMap(position, sourceMap) {
let smc;
if(! (sourceMap instanceof SourceMapConsumer)) {
smc = new SourceMapConsumer(sourceMap);
} else {
smc = sourceMap;
}
let resolvedPosition = smc.originalPositionFor(position);
if(!resolvedPosition.source) {
return null;
}
let sourceContent;
try {
sourceContent = smc.sourceContentFor(resolvedPosition.source);
} catch(err) {
return resolvedPosition;
}
if(sourceMappingURL.existsIn(sourceContent)) {
let url = sourceMappingURL.getFrom(sourceContent);
if(!url.match(/^data:application\.json;base64,/)) {
return resolvedPosition;
}
sourceMap = JSON.parse(extractDataUrl(url));
sourceMap = new SourceMapConsumer(sourceMap);
return resolveSourceMap({
line: resolvedPosition.line,
column: resolvedPosition.column
}, sourceMap);
} else {
return resolvedPosition;
}
}
function resolveStackTraceNode(sourceMapLocation, errorStack) {
let sourceMapBase = path.parse(sourceMapLocation).base;
let belongsToSourceMap = line => {
let m;
if((m = line.match(/(?:at |\()(\/.*\.js)/))) {
let parsed = path.parse(m[1]);
if(parsed.base + '.map' === sourceMapBase) {
return true;
}
}
};
return readSourceMap(sourceMapLocation).then(smc => {
return errorStack.split(/\n/).map(line => {
if(!belongsToSourceMap(line)) {
return line;
}
let replacedLine = line.replace(/at (.*?) \((.*?):(\d+):(\d+)\)/, (m, symbol, file, line, column) => {
let resolvedPosition = resolveSourceMap({
line: parseInt(line),
column: parseInt(column)
}, smc);
if(!resolvedPosition) { return `at ${symbol} (${file}:${line}:${column})`; }
return `at ${resolvedPosition.name} (${resolvedPosition.source}:${resolvedPosition.line}:${resolvedPosition.column})`;
});
if(replacedLine != line) {
return replacedLine;
}
return line.replace(/at (.*?):(\d+):(\d+)/, (m, file, line, column) => {
let resolvedPosition = resolveSourceMap({
line: parseInt(line),
column: parseInt(column)
}, smc);
if(!resolvedPosition) { return `at ${file}:${line}:${column}`; }
return `at ${resolvedPosition.source}:${resolvedPosition.line}:${resolvedPosition.column}`;
});
}).join('\n');
});
}
function resolveStackTrace(sourceMapLocation, errorStack) {
return readSourceMap(sourceMapLocation).then(smc => {
return ErrorStackParser.parse({stack: errorStack}).map(sf => {
let line = sf.lineNumber;
let column = sf.columnNumber;
let resolvedPosition = resolveSourceMap({
line: parseInt(line),
column: parseInt(column)
}, smc);
if(!resolvedPosition) {
return sf.toString();
}
sf.setLineNumber(resolvedPosition.line);
sf.setColumnNumber(resolvedPosition.column);
if(resolvedPosition.name) {
sf.setFunctionName(resolvedPosition.name);
}
sf.setFileName(resolvedPosition.source);
return sf.toString();
}).join('\n');
});
}
module.exports = resolveStackTrace;
module.exports.node = resolveStackTraceNode;