UNPKG

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