|
134 | 134 | }
|
135 | 135 | }
|
136 | 136 |
|
137 |
| - |
138 | 137 | function _extractLocationInfoFromSourceMapSource(stackframe, sourceMapConsumer, sourceCache) {
|
139 | 138 | return new Promise(function(resolve, reject) {
|
140 | 139 | var loc = sourceMapConsumer.originalPositionFor({
|
|
143 | 142 | });
|
144 | 143 |
|
145 | 144 | if (loc.source) {
|
| 145 | + // cache mapped sources |
146 | 146 | var mappedSource = sourceMapConsumer.sourceContentFor(loc.source);
|
147 | 147 | if (mappedSource) {
|
148 | 148 | sourceCache[loc.source] = mappedSource;
|
149 | 149 | }
|
| 150 | + |
150 | 151 | resolve(
|
| 152 | + // given stackframe and source location, update stackframe |
151 | 153 | new StackFrame({
|
152 | 154 | functionName: loc.name || stackframe.functionName,
|
153 | 155 | args: stackframe.args,
|
|
216 | 218 | }.bind(this));
|
217 | 219 | };
|
218 | 220 |
|
| 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) { |
| 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 | + |
219 | 252 | /**
|
220 | 253 | * Given a StackFrame, enhance function name and use source maps for a
|
221 | 254 | * better StackFrame.
|
|
278 | 311 | _ensureStackFrameIsLegit(stackframe);
|
279 | 312 |
|
280 | 313 | var sourceCache = this.sourceCache;
|
281 |
| - var sourceMapConsumerCache = this.sourceMapConsumerCache; |
282 | 314 | var fileName = stackframe.fileName;
|
283 | 315 | this._get(fileName).then(function(source) {
|
284 | 316 | var sourceMappingURL = _findSourceMappingURL(source);
|
285 | 317 | var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:';
|
286 |
| - var base = fileName.substring(0, fileName.lastIndexOf('/') + 1); |
| 318 | + var defaultSourceRoot = fileName.substring(0, fileName.lastIndexOf('/') + 1); |
287 | 319 |
|
288 | 320 | if (sourceMappingURL[0] !== '/' && !isDataUrl && !(/^https?:\/\/|^\/\//i).test(sourceMappingURL)) {
|
289 |
| - sourceMappingURL = base + sourceMappingURL; |
290 |
| - } |
291 |
| - |
292 |
| - var cachedSourceMapConsumer = sourceMapConsumerCache[sourceMappingURL]; |
293 |
| - if (cachedSourceMapConsumer !== undefined) { |
294 |
| - return _extractLocationInfoFromSourceMapSource(stackframe, cachedSourceMapConsumer, sourceCache) |
295 |
| - .then(resolve)['catch'](function() { |
296 |
| - resolve(stackframe); |
297 |
| - }); |
| 321 | + sourceMappingURL = defaultSourceRoot + sourceMappingURL; |
298 | 322 | }
|
299 | 323 |
|
300 |
| - return this._get(sourceMappingURL).then(function(sourceMapSource) { |
301 |
| - if (typeof sourceMapSource === 'string') { |
302 |
| - sourceMapSource = _parseJson(sourceMapSource.replace(/^\)\]\}'/, '')); |
303 |
| - } |
304 |
| - if (typeof sourceMapSource.sourceRoot === 'undefined') { |
305 |
| - sourceMapSource.sourceRoot = base; |
306 |
| - } |
307 |
| - |
308 |
| - var sourceMapConsumer = new SourceMap.SourceMapConsumer(sourceMapSource); |
309 |
| - sourc
76BD
eMapConsumerCache[sourceMappingURL] = sourceMapConsumer; |
| 324 | + return this._getSourceMapConsumer(sourceMappingURL, defaultSourceRoot).then(function(sourceMapConsumer) { |
310 | 325 | return _extractLocationInfoFromSourceMapSource(stackframe, sourceMapConsumer, sourceCache)
|
311 | 326 | .then(resolve)['catch'](function() {
|
312 | 327 | resolve(stackframe);
|
313 | 328 | });
|
314 |
| - }, reject)['catch'](reject); |
| 329 | + }); |
315 | 330 | }.bind(this), reject)['catch'](reject);
|
316 | 331 | }.bind(this));
|
317 | 332 | };
|
|
0 commit comments