Skip to content

Commit 4b035c1

Browse files
committed
Update dist/ with fixes for #41
1 parent 8223c8d commit 4b035c1

5 files changed

+83
-52
lines changed

dist/stacktrace-gps-with-polyfills.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/stacktrace-gps-with-polyfills.min.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/stacktrace-gps.js

+79-48
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
req.onerror = reject;
2727
req.onreadystatechange = function onreadystatechange() {
2828
if (req.readyState === 4) {
29-
if (req.status >= 200 && req.status < 300) {
29+
if ((req.status >= 200 && req.status < 300) ||
30+
(url.substr(0, 7) === 'file://' && req.responseText)) {
3031
resolve(req.responseText);
3132
} else {
3233
reject(new Error('HTTP status: ' + req.status + ' retrieving ' + url));
@@ -62,18 +63,23 @@
6263
}
6364

6465
function _findFunctionName(source, lineNumber/*, columnNumber*/) {
65-
// function {name}({args}) m[1]=name m[2]=args
66-
var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
67-
// {name} = function ({args}) TODO args capture
68-
var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
69-
// {name} = eval()
70-
var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
66+
var syntaxes = [
67+
// {name} = function ({args}) TODO args capture
68+
/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/,
69+
// function {name}({args}) m[1]=name m[2]=args
70+
/function\s+([^('"`]*?)\s*\(([^)]*)\)/,
71+
// {name} = eval()
72+
/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/,
73+
// fn_name() {
74+
/\b(?!(?:if|for|switch|while|with|catch)\b)(?:(?:static)\s+)?(\S+)\s*\(.*?\)\s*\{/,
75+
// {name} = () => {
76+
/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*\(.*?\)\s*=>/
77+
];
7178
var lines = source.split('\n');
7279

7380
// Walk backwards in the source lines until we find the line which matches one of the patterns above
7481
var code = '';
7582
var maxLines = Math.min(lineNumber, 20);
76-
var m;
7783
for (var i = 0; i < maxLines; ++i) {
7884
// lineNo is 1-based, source[] is 0-based
7985
var line = lines[lineNumber - i - 1];
@@ -84,17 +90,12 @@
8490

8591
if (line) {
8692
code = line + code;
87-
m = reFunctionExpression.exec(code);
88-
if (m && m[1]) {
89-
return m[1];
90-
}
91-
m = reFunctionDeclaration.exec(code);
92-
if (m && m[1]) {
93-
return m[1];
94-
}
95-
m = reFunctionEvaluation.exec(code);
96-
if (m && m[1]) {
97-
return m[1];
93+
var len = syntaxes.length;
94+
for (var index = 0; index < len; index++) {
95+
var m = syntaxes[index].exec(code);
96+
if (m && m[1]) {
97+
return m[1];
98+
}
9899
}
99100
}
100101
}
@@ -125,35 +126,37 @@
125126
}
126127

127128
function _findSourceMappingURL(source) {
128-
var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/.exec(source);
129+
var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/m.exec(source);
129130
if (m && m[1]) {
130131
return m[1];
131132
} else {
132133
throw new Error('sourceMappingURL not found');
133134
}
134135
}
135136

136-
function _extractLocationInfoFromSourceMap(stackframe, rawSourceMap, sourceCache) {
137+
function _extractLocationInfoFromSourceMapSource(stackframe, sourceMapConsumer, sourceCache) {
137138
return new Promise(function(resolve, reject) {
138-
var mapConsumer = new SourceMap.SourceMapConsumer(rawSourceMap);
139-
140-
var loc = mapConsumer.originalPositionFor({
139+
var loc = sourceMapConsumer.originalPositionFor({
141140
line: stackframe.lineNumber,
142141
column: stackframe.columnNumber
143142
});
144143

145144
if (loc.source) {
146-
var mappedSource = mapConsumer.sourceContentFor(loc.source);
145+
// cache mapped sources
146+
var mappedSource = sourceMapConsumer.sourceContentFor(loc.source);
147147
if (mappedSource) {
148148
sourceCache[loc.source] = mappedSource;
149149
}
150+
150151
resolve(
151-
new StackFrame(
152-
loc.name || stackframe.functionName,
153-
stackframe.args,
154-
loc.source,
155-
loc.line,
156-
loc.column));
152+
// given stackframe and source location, update stackframe
153+
new StackFrame({
154+
functionName: loc.name || stackframe.functionName,
155+
args: stackframe.args,
156+
fileName: loc.source,
157+
lineNumber: loc.line,
158+
columnNumber: loc.column
159+
}));
157160
} else {
158161
reject(new Error('Could not get original source for given stackframe and source map'));
159162
}
@@ -164,6 +167,7 @@
164167
* @constructor
165168
* @param {Object} opts
166169
* opts.sourceCache = {url: "Source String"} => preload source cache
170+
* opts.sourceMapConsumerCache = {/path/file.js.map: SourceMapConsumer}
167171
* opts.offline = True to prevent network requests.
168172
* Best effort without sources or source maps.
169173
* opts.ajax = Promise returning function to make X-Domain requests
@@ -175,6 +179,7 @@
175179
opts = opts || {};
176180

177181
this.sourceCache = opts.sourceCache || {};
182+
this.sourceMapConsumerCache = opts.sourceMapConsumerCache || {};
178183

179184
this.ajax = opts.ajax || _xdr;
180185

@@ -213,6 +218,37 @@
213218
}.bind(this));
214219
};
215220

221+
/**
222+
* Creating SourceMapConsumers is expensive, so this wraps the creation of a
223+
* SourceMapConsumer in a per-instance cache.
224+
*
225+
* @param sourceMappingURL = {String} URL to fetch source map from
226+
* @param defaultSourceRoot = Default source root for source map if undefined
227+
* @returns {Promise} that resolves a SourceMapConsumer
228+
*/
229+
this._getSourceMapConsumer = function _getSourceMapConsumer(sourceMappingURL, defaultSourceRoot) {
230+
return new Promise(function(resolve, reject) {
231+
if (this.sourceMapConsumerCache[sourceMappingURL]) {
232+
resolve(this.sourceMapConsumerCache[sourceMappingURL]);
233+
} else {
234+
var sourceMapConsumerPromise = new Promise(function(resolve, reject) {
235+
return this._get(sourceMappingURL).then(function(sourceMapSource) {
236+
if (typeof sourceMapSource === 'string') {
237+
sourceMapSource = _parseJson(sourceMapSource.replace(/^\)\]\}'/, ''));
238+
}
239+
if (typeof sourceMapSource.sourceRoot === 'undefined') {
240+
sourceMapSource.sourceRoot = defaultSourceRoot;
241+
}
242+
243+
resolve(new SourceMap.SourceMapConsumer(sourceMapSource));
244+
}, reject);
245+
}.bind(this));
246+
this.sourceMapConsumerCache[sourceMappingURL] = sourceMapConsumerPromise;
247+
resolve(sourceMapConsumerPromise);
248+
}
249+
}.bind(this));
250+
};
251+
216252
/**
217253
* Given a StackFrame, enhance function name and use source maps for a
218254
* better StackFrame.
@@ -249,11 +285,13 @@
249285
var guessedFunctionName = _findFunctionName(source, lineNumber, columnNumber);
250286
// Only replace functionName if we found something
251287
if (guessedFunctionName) {
252-
resolve(new StackFrame(guessedFunctionName,
253-
stackframe.args,
254-
stackframe.fileName,
255-
lineNumber,
256-
columnNumber));
288+
resolve(new StackFrame({
289+
functionName: guessedFunctionName,
290+
args: stackframe.args,
291+
fileName: stackframe.fileName,
292+
lineNumber: lineNumber,
293+
columnNumber: columnNumber
294+
}));
257295
} else {
258296
resolve(stackframe);
259297
}
@@ -277,25 +315,18 @@
277315
this._get(fileName).then(function(source) {
278316
var sourceMappingURL = _findSourceMappingURL(source);
279317
var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:';
280-
var base = fileName.substring(0, fileName.lastIndexOf('/') + 1);
318+
var defaultSourceRoot = fileName.substring(0, fileName.lastIndexOf('/') + 1);
281319

282320
if (sourceMappingURL[0] !== '/' && !isDataUrl && !(/^https?:\/\/|^\/\//i).test(sourceMappingURL)) {
283-
sourceMappingURL = base + sourceMappingURL;
321+
sourceMappingURL = defaultSourceRoot + sourceMappingURL;
284322
}
285323

286-
this._get(sourceMappingURL).then(function(sourceMap) {
287-
if (typeof sourceMap === 'string') {
288-
sourceMap = _parseJson(sourceMap.replace(/^\)\]\}'/, ''));
289-
}
290-
if (typeof sourceMap.sourceRoot === 'undefined') {
291-
sourceMap.sourceRoot = base;
292-
}
293-
294-
_extractLocationInfoFromSourceMap(stackframe, sourceMap, sourceCache)
324+
return this._getSourceMapConsumer(sourceMappingURL, defaultSourceRoot).then(function(sourceMapConsumer) {
325+
return _extractLocationInfoFromSourceMapSource(stackframe, sourceMapConsumer, sourceCache)
295326
.then(resolve)['catch'](function() {
296327
resolve(stackframe);
297328
});
298-
}, reject)['catch'](reject);
329+
});
299330
}.bind(this), reject)['catch'](reject);
300331
}.bind(this));
301332
};

0 commit comments

Comments
 (0)