|
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 | | - sourceMapConsumerCache[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