diff --git a/source-map-support.js b/source-map-support.js index a9360bd..bfddefc 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -2,38 +2,58 @@ var SourceMapConsumer = require('source-map').SourceMapConsumer; var path = require('path'); var fs = require('fs'); -exports.mapSourcePosition = mapSourcePosition = function(cache, position) { +// Can be overridden by the retrieveSourceMap option to install. Takes a +// generated source filename; returns a {map, optional url} object, or null if +// there is no source map. The map field may be either a string or the parsed +// JSON object (ie, it must be a valid argument to the SourceMapConsumer +// constructor). +var retrieveSourceMap = function (source) { + if (!fs.existsSync(source)) + return null; + + // Get the URL of the source map + var fileData = fs.readFileSync(source, 'utf8'); + var match = /\/\/[#@]\s*sourceMappingURL=(.*)\s*$/m.exec(fileData); + if (!match) return null; + var sourceMappingURL = match[1]; + + // Read the contents of the source map + var sourceMapData; + var dataUrlPrefix = "data:application/json;base64,"; + if (sourceMappingURL.slice(0, dataUrlPrefix.length).toLowerCase() == dataUrlPrefix) { + // Support source map URL as a data url + sourceMapData = new Buffer(sourceMappingURL.slice(dataUrlPrefix.length), "base64").toString(); + } + else { + // Support source map URLs relative to the source URL + var dir = path.dirname(source); + sourceMappingURL = path.resolve(dir, sourceMappingURL); + + if (fs.existsSync(sourceMappingURL)) { + sourceMapData = fs.readFileSync(sourceMappingURL, 'utf8'); + } + } + + if (!sourceMapData) { + return null; + } + + return { + url: sourceMappingURL, + map: sourceMapData + }; +}; + +var mapSourcePosition = exports.mapSourcePosition = function(cache, position) { var sourceMap = cache[position.source]; - if (!sourceMap && fs.existsSync(position.source)) { - // Get the URL of the source map - var fileData = fs.readFileSync(position.source, 'utf8'); - var match = /\/\/[#@]\s*sourceMappingURL=(.*)\s*$/m.exec(fileData); - if (!match) return position; - var sourceMappingURL = match[1]; - - // Read the contents of the source map - var sourceMapData; - var dataUrlPrefix = "data:application/json;base64,"; - if (sourceMappingURL.slice(0, dataUrlPrefix.length).toLowerCase() == dataUrlPrefix) { - // Support source map URL as a data url - sourceMapData = new Buffer(sourceMappingURL.slice(dataUrlPrefix.length), "base64").toString(); - } - else { - // Support source map URLs relative to the source URL - var dir = path.dirname(position.source); - sourceMappingURL = path.resolve(dir, sourceMappingURL); - - if (fs.existsSync(sourceMappingURL)) { - sourceMapData = fs.readFileSync(sourceMappingURL, 'utf8'); - } - } - - if (sourceMapData) { - sourceMap = { - url: sourceMappingURL, - map: new SourceMapConsumer(sourceMapData) + if (!sourceMap) { + // Call the (overrideable) retrieveSourceMap function to get the source map. + var urlAndMap = retrieveSourceMap(position.source); + if (urlAndMap) { + sourceMap = cache[position.source] = { + url: urlAndMap.url, + map: new SourceMapConsumer(urlAndMap.map) }; - cache[position.source] = sourceMap; } } @@ -47,13 +67,15 @@ exports.mapSourcePosition = mapSourcePosition = function(cache, position) { // better to give a precise location in the compiled file than a vague // location in the original file. if (originalPosition.source !== null) { - originalPosition.source = path.resolve(path.dirname(sourceMap.url), originalPosition.source); + if (sourceMap.url) { + originalPosition.source = path.resolve(path.dirname(sourceMap.url), originalPosition.source); + } return originalPosition; } } return position; -} +}; // Parses code generated by FormatEvalOrigin(), a function inside V8: // https://code.google.com/p/v8/source/browse/trunk/src/messages.js @@ -162,6 +184,11 @@ exports.install = function(options) { var installHandler = 'handleUncaughtExceptions' in options ? options.handleUncaughtExceptions : true; + // Allow source maps to be found by methods other than reading the files + // directly from disk. + if (options.retrieveSourceMap) + retrieveSourceMap = options.retrieveSourceMap; + // Provide the option to not install the uncaught exception handler. This is // to support other uncaught exception handlers (in test frameworks, for // example). If this handler is not installed and there are no other uncaught