Skip to content

Commit e173aff

Browse files
committed
Cache and reuse SourceMapConsumer for same source
Issue: #41
1 parent c61d2b0 commit e173aff

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

spec/stacktrace-gps-spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,23 @@ describe('StackTraceGPS', function() {
352352
}
353353
});
354354
});
355+
356+
describe('given cache entry for source map', function() {
357+
it('resolves SourceMapConsumer from cache', function(done) {
358+
var stackframe = new StackFrame({args: [], fileName: 'http://localhost:9999/file.min.js', lineNumber: 1, columnNumber: 4});
359+
var sourceCache = {'http://localhost:9999/file.min.js': 'var foo=function(){};function bar(){}var baz=eval("XXX");\n//# sourceMappingURL=file.js.map'};
360+
var sourceMap = '{"version":3,"sources":["./file.js"],"sourceRoot":"http://localhost:9999/","names":["foo","bar","baz","eval"],"mappings":"AAAA,GAAIA,KAAM,YACV,SAASC,QACT,GAAIC,KAAMC,KAAK","file":"file.min.js"}';
361+
var sourceMapConsumerCache = {'http://localhost:9999/file.js.map': new SourceMap.SourceMapConsumer(sourceMap)};
362+
new StackTraceGPS({sourceCache: sourceCache, sourceMapConsumerCache: sourceMapConsumerCache})
363+
.getMappedLocation(stackframe)
364+
.then(callback, done.fail);
365+
366+
function callback(stackframe) {
367+
expect(stackframe).toEqual(new StackFrame({functionName: 'foo', args: [], fileName: 'http://localhost:9999/file.js', lineNumber: 1, columnNumber: 4}));
368+
done();
369+
}
370+
});
371+
});
355372
});
356373

357374
describe('#pinpoint', function() {

stacktrace-gps.js

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,17 @@
133133
}
134134
}
135135

136-
function _extractLocationInfoFromSourceMap(stackframe, rawSourceMap, sourceCache) {
137-
return new Promise(function(resolve, reject) {
138-
var mapConsumer = new SourceMap.SourceMapConsumer(rawSourceMap);
139136

140-
var loc = mapConsumer.originalPositionFor({
137+
138+
function _extractLocationInfoFromSourceMapSource(stackframe, sourceMapConsumer, sourceCache) {
139+
return new Promise(function(resolve, reject) {
140+
var loc = sourceMapConsumer.originalPositionFor({
141141
line: stackframe.lineNumber,
142142
column: stackframe.columnNumber
143143
});
144144

145145
if (loc.source) {
146-
var mappedSource = mapConsumer.sourceContentFor(loc.source);
146+
var mappedSource = sourceMapConsumer.sourceContentFor(loc.source);
147147
if (mappedSource) {
148148
sourceCache[loc.source] = mappedSource;
149149
}
@@ -165,6 +165,7 @@
165165
* @constructor
166166
* @param {Object} opts
167167
* opts.sourceCache = {url: "Source String"} => preload source cache
168+
* opts.sourceMapConsumerCache = {/path/file.js.map: SourceMapConsumer}
168169
* opts.offline = True to prevent network requests.
169170
* Best effort without sources or source maps.
170171
* opts.ajax = Promise returning function to make X-Domain requests
@@ -176,6 +177,7 @@
176177
opts = opts || {};
177178

178179
this.sourceCache = opts.sourceCache || {};
180+
this.sourceMapConsumerCache = opts.sourceMapConsumerCache || {};
179181

180182
this.ajax = opts.ajax || _xdr;
181183

@@ -276,6 +278,7 @@
276278
_ensureStackFrameIsLegit(stackframe);
277279

278280
var sourceCache = this.sourceCache;
281+
var sourceMapConsumerCache = this.sourceMapConsumerCache;
279282
var fileName = stackframe.fileName;
280283
this._get(fileName).then(function(source) {
281284
var sourceMappingURL = _findSourceMappingURL(source);
@@ -286,15 +289,25 @@
286289
sourceMappingURL = base + sourceMappingURL;
287290
}
288291

289-
return this._get(sourceMappingURL).then(function(sourceMap) {
290-
if (typeof sourceMap === 'string') {
291-
sourceMap = _parseJson(sourceMap.replace(/^\)\]\}'/, ''));
292+
var cachedSourceMapConsumer = sourceMapConsumerCache[sourceMappingURL];
293+
if (cachedSourceMapConsumer !== undefined) {
294+
return _extractLocationInfoFromSourceMapSource(stackframe, cachedSourceMapConsumer, sourceCache)
295+
.then(resolve)['catch'](function() {
296+
resolve(stackframe);
297+
});
298+
}
299+
300+
return this._get(sourceMappingURL).then(function(sourceMapSource) {
301+
if (typeof sourceMapSource === 'string') {
302+
sourceMapSource = _parseJson(sourceMapSource.replace(/^\)\]\}'/, ''));
292303
}
293-
if (typeof sourceMap.sourceRoot === 'undefined') {
294-
sourceMap.sourceRoot = base;
304+
if (typeof sourceMapSource.sourceRoot === 'undefined') {
305+
sourceMapSource.sourceRoot = base;
295306
}
296307

297-
return _extractLocationInfoFromSourceMap(stackframe, sourceMap, sourceCache)
308+
var sourceMapConsumer = new SourceMap.SourceMapConsumer(sourceMapSource);
309+
sourceMapConsumerCache[sourceMappingURL] = sourceMapConsumer;
310+
return _extractLocationInfoFromSourceMapSource(stackframe, sourceMapConsumer, sourceCache)
298311
.then(resolve)['catch'](function() {
299312
resolve(stackframe);
300313
});

0 commit comments

Comments
 (0)