UNPKG

8.85 kBJavaScriptView Raw
1Object.defineProperty(exports, "__esModule", { value: true });
2var utils_1 = require("@sentry/utils");
3var fs_1 = require("fs");
4var lru_map_1 = require("lru_map");
5var stacktrace = require("./stacktrace");
6// tslint:disable-next-line:no-unsafe-any
7var DEFAULT_LINES_OF_CONTEXT = 7;
8var FILE_CONTENT_CACHE = new lru_map_1.LRUMap(100);
9/**
10 * Resets the file cache. Exists for testing purposes.
11 * @hidden
12 */
13function resetFileContentCache() {
14 FILE_CONTENT_CACHE.clear();
15}
16exports.resetFileContentCache = resetFileContentCache;
17/** JSDoc */
18function getFunction(frame) {
19 try {
20 return frame.functionName || frame.typeName + "." + (frame.methodName || '<anonymous>');
21 }
22 catch (e) {
23 // This seems to happen sometimes when using 'use strict',
24 // stemming from `getTypeName`.
25 // [TypeError: Cannot read property 'constructor' of undefined]
26 return '<anonymous>';
27 }
28}
29var mainModule = ((require.main && require.main.filename && utils_1.dirname(require.main.filename)) ||
30 global.process.cwd()) + "/";
31/** JSDoc */
32function getModule(filename, base) {
33 if (!base) {
34 base = mainModule; // tslint:disable-line:no-parameter-reassignment
35 }
36 // It's specifically a module
37 var file = utils_1.basename(filename, '.js');
38 filename = utils_1.dirname(filename); // tslint:disable-line:no-parameter-reassignment
39 var n = filename.lastIndexOf('/node_modules/');
40 if (n > -1) {
41 // /node_modules/ is 14 chars
42 return filename.substr(n + 14).replace(/\//g, '.') + ":" + file;
43 }
44 // Let's see if it's a part of the main module
45 // To be a part of main module, it has to share the same base
46 n = (filename + "/").lastIndexOf(base, 0);
47 if (n === 0) {
48 var moduleName = filename.substr(base.length).replace(/\//g, '.');
49 if (moduleName) {
50 moduleName += ':';
51 }
52 moduleName += file;
53 return moduleName;
54 }
55 return file;
56}
57/**
58 * This function reads file contents and caches them in a global LRU cache.
59 * Returns a Promise filepath => content array for all files that we were able to read.
60 *
61 * @param filenames Array of filepaths to read content from.
62 */
63function readSourceFiles(filenames) {
64 // we're relying on filenames being de-duped already
65 if (filenames.length === 0) {
66 return utils_1.SyncPromise.resolve({});
67 }
68 return new utils_1.SyncPromise(function (resolve) {
69 var sourceFiles = {};
70 var count = 0;
71 var _loop_1 = function (i) {
72 var filename = filenames[i];
73 var cache = FILE_CONTENT_CACHE.get(filename);
74 // We have a cache hit
75 if (cache !== undefined) {
76 // If it's not null (which means we found a file and have a content)
77 // we set the content and return it later.
78 if (cache !== null) {
79 sourceFiles[filename] = cache;
80 }
81 count++;
82 // In any case we want to skip here then since we have a content already or we couldn't
83 // read the file and don't want to try again.
84 if (count === filenames.length) {
85 resolve(sourceFiles);
86 }
87 return "continue";
88 }
89 fs_1.readFile(filename, function (err, data) {
90 var content = err ? null : data.toString();
91 sourceFiles[filename] = content;
92 // We always want to set the cache, even to null which means there was an error reading the file.
93 // We do not want to try to read the file again.
94 FILE_CONTENT_CACHE.set(filename, content);
95 count++;
96 if (count === filenames.length) {
97 resolve(sourceFiles);
98 }
99 });
100 };
101 // tslint:disable-next-line:prefer-for-of
102 for (var i = 0; i < filenames.length; i++) {
103 _loop_1(i);
104 }
105 });
106}
107/**
108 * @hidden
109 */
110function extractStackFromError(error) {
111 var stack = stacktrace.parse(error);
112 if (!stack) {
113 return [];
114 }
115 return stack;
116}
117exports.extractStackFromError = extractStackFromError;
118/**
119 * @hidden
120 */
121function parseStack(stack, options) {
122 var filesToRead = [];
123 var linesOfContext = options && options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;
124 var frames = stack.map(function (frame) {
125 var parsedFrame = {
126 colno: frame.columnNumber,
127 filename: frame.fileName || '',
128 function: getFunction(frame),
129 lineno: frame.lineNumber,
130 };
131 var isInternal = frame.native ||
132 (parsedFrame.filename &&
133 !parsedFrame.filename.startsWith('/') &&
134 !parsedFrame.filename.startsWith('.') &&
135 parsedFrame.filename.indexOf(':\\') !== 1);
136 // in_app is all that's not an internal Node function or a module within node_modules
137 // note that isNative appears to return true even for node core libraries
138 // see https://github.com/getsentry/raven-node/issues/176
139 parsedFrame.in_app =
140 !isInternal && parsedFrame.filename !== undefined && parsedFrame.filename.indexOf('node_modules/') === -1;
141 // Extract a module name based on the filename
142 if (parsedFrame.filename) {
143 parsedFrame.module = getModule(parsedFrame.filename);
144 if (!isInternal && linesOfContext > 0) {
145 filesToRead.push(parsedFrame.filename);
146 }
147 }
148 return parsedFrame;
149 });
150 // We do an early return if we do not want to fetch context liens
151 if (linesOfContext <= 0) {
152 return utils_1.SyncPromise.resolve(frames);
153 }
154 try {
155 return addPrePostContext(filesToRead, frames, linesOfContext);
156 }
157 catch (_) {
158 // This happens in electron for example where we are not able to read files from asar.
159 // So it's fine, we recover be just returning all frames without pre/post context.
160 return utils_1.SyncPromise.resolve(frames);
161 }
162}
163exports.parseStack = parseStack;
164/**
165 * This function tries to read the source files + adding pre and post context (source code)
166 * to a frame.
167 * @param filesToRead string[] of filepaths
168 * @param frames StackFrame[] containg all frames
169 */
170function addPrePostContext(filesToRead, frames, linesOfContext) {
171 return new utils_1.SyncPromise(function (resolve) {
172 return readSourceFiles(filesToRead).then(function (sourceFiles) {
173 var result = frames.map(function (frame) {
174 if (frame.filename && sourceFiles[frame.filename]) {
175 try {
176 var lines = sourceFiles[frame.filename].split('\n');
177 utils_1.addContextToFrame(lines, frame, linesOfContext);
178 }
179 catch (e) {
180 // anomaly, being defensive in case
181 // unlikely to ever happen in practice but can definitely happen in theory
182 }
183 }
184 return frame;
185 });
186 resolve(result);
187 });
188 });
189}
190/**
191 * @hidden
192 */
193function getExceptionFromError(error, options) {
194 var name = error.name || error.constructor.name;
195 var stack = extractStackFromError(error);
196 return new utils_1.SyncPromise(function (resolve) {
197 return parseStack(stack, options).then(function (frames) {
198 var result = {
199 stacktrace: {
200 frames: prepareFramesForEvent(frames),
201 },
202 type: name,
203 value: error.message,
204 };
205 resolve(result);
206 });
207 });
208}
209exports.getExceptionFromError = getExceptionFromError;
210/**
211 * @hidden
212 */
213function parseError(error, options) {
214 return new utils_1.SyncPromise(function (resolve) {
215 return getExceptionFromError(error, options).then(function (exception) {
216 resolve({
217 exception: {
218 values: [exception],
219 },
220 });
221 });
222 });
223}
224exports.parseError = parseError;
225/**
226 * @hidden
227 */
228function prepareFramesForEvent(stack) {
229 if (!stack || !stack.length) {
230 return [];
231 }
232 var localStack = stack;
233 var firstFrameFunction = localStack[0].function || '';
234 if (firstFrameFunction.indexOf('captureMessage') !== -1 || firstFrameFunction.indexOf('captureException') !== -1) {
235 localStack = localStack.slice(1);
236 }
237 // The frame where the crash happened, should be the last entry in the array
238 return localStack.reverse();
239}
240exports.prepareFramesForEvent = prepareFramesForEvent;
241//# sourceMappingURL=parsers.js.map
\No newline at end of file