Allow overriding the mapping from source file to source map.

This is done with the new retrieveSourceMap option to sourcemap.install.

This is useful if you have another way of finding your source maps (and can avoid sync reads).

Also, allow source maps to not have an URL, and don't write to a global named mapSourcePosition.
This commit is contained in:
David Glasser 2013-07-08 17:17:17 -07:00
parent 51db253332
commit 1cf5ce0f7d

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