Merge pull request #18 from meteor/retrieve-source-map

Allow overriding the mapping from source file to source map.
This commit is contained in:
Evan Wallace 2013-07-20 15:22:12 -07:00
commit bdf2ffb6a4

View File

@ -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