Skip to content
This repository has been archived by the owner on Oct 9, 2020. It is now read-only.

Commit

Permalink
fix incorrect concatenation of source maps
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-startsev committed Sep 16, 2016
1 parent 2649e18 commit a49584b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 147 deletions.
76 changes: 23 additions & 53 deletions lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,69 +4,39 @@ var fs = require('fs');
var Promise = require('bluebird');
var asp = require('bluebird').promisify;
var extend = require('./utils').extend;

var fromFileURL = require('./utils').fromFileURL;
var toFileURL = require('./utils').toFileURL;

function countLines(str) {
return str.split(/\r\n|\r|\n/).length;
}

// Process compiler outputs, gathering:
//
// concatOutputs: list of source strings to concatenate
// sourceMapsWithOffsets: list of [absolute offset,
// source map string] pairs
//
// Takes lists as empty references and populates via push.
function processOutputs(outputs) {
var removeSourceMaps = require('./sourcemaps').removeSourceMaps;

var offset = 0;

var outputObj = {};

var sources = outputObj.sources = [];
var sourceMapsWithOffsets = outputObj.sourceMapsWithOffsets = [];

outputs.forEach(function(output) {
var source;
if (typeof output == 'object') {
source = output.source || '';
var offset_ = output.sourceMapOffset || 0;
var map = output.sourceMap;
if (map) {
sourceMapsWithOffsets.push([offset + offset_, map]);
}
}
// NB perhaps we should enforce output is always an object down the chain?
else if (typeof output == 'string') {
source = output;
}
else {
throw "Unexpected output format: " + output.toString();
function createOutput(outFile, outputs, basePath, sourceMaps, sourceMapContents) {
var concatenate = require('./sourcemaps').concatenate;
var files = outputs.map(function (el) {
if (el.source) {
//normalize source urls
el.sourceMap.sources = el.sourceMap.sources.map(function (sourceURL) {
return sourceURL.replace(/\\/g, "/");
});
return {
code: el.source,
map: el.sourceMap
};
} else {
return {
code: el,
map: undefined
};
}
source = removeSourceMaps(source || '');
offset += countLines(source);
sources.push(source);
});

return outputObj;
}

function createOutput(outFile, outputs, basePath, sourceMaps, sourceMapContents) {
var concatenateSourceMaps = require('./sourcemaps').concatenateSourceMaps;

var outputObj = processOutputs(outputs);

if (sourceMaps)
var sourceMap = concatenateSourceMaps(outFile, outputObj.sourceMapsWithOffsets, basePath, sourceMapContents);

var output = outputObj.sources.join('\n');
//concatenates sources and appropriate source maps
var concatenated = concatenate(files, outFile).toStringWithSourceMap({
file: path.basename(outFile)
});

return {
source: output,
sourceMap: sourceMap
source: concatenated.code,
sourceMap: concatenated.map.toString()
};
}

Expand Down
119 changes: 25 additions & 94 deletions lib/sourcemaps.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,36 @@
var sourceMap = require('source-map');
var SourceNode = sourceMap.SourceNode;
var SourceMapConsumer = sourceMap.SourceMapConsumer;
var path = require('path');
var fs = require('fs');

var toFileURL = require('./utils').toFileURL;
var fromFileURL = require('./utils').fromFileURL;
exports.concatenate = function(files, outFile) {
var concatenated = new SourceNode();

var wrapSourceMap = function(map) {
return new sourceMap.SourceMapConsumer(map);
};

var sourceMapRegEx = /\/\/[@#] ?(sourceURL|sourceMappingURL)=([^\n'"]+)/;
exports.removeSourceMaps = function(source) {
return source.replace(sourceMapRegEx, '');
};

function getMapObject(map) {
if (typeof map != 'string')
return map;

try {
return JSON.parse(map);
}
catch(error) {
throw new Error('Invalid JSON: ' + map);
}
}

function isFileURL(url) {
return url.substr(0, 8) == 'file:///';
}

exports.concatenateSourceMaps = function(outFile, mapsWithOffsets, basePath, sourceMapContents) {
var generated = new sourceMap.SourceMapGenerator({
file: path.basename(outFile)
});

var outPath = path.dirname(outFile);

var contentsBySource = sourceMapContents ? {} : null;

mapsWithOffsets.forEach(function(pair) {
var offset = pair[0];
var map = getMapObject(pair[1]);

if (sourceMapContents && map.sourcesContent) {
for (var i=0; i<map.sources.length; i++) {
var source = (map.sourceRoot || '') + map.sources[i];
if (!source.match(/\/@traceur/)) {
if (!contentsBySource[source]) {
contentsBySource[source] = map.sourcesContent[i];
} else {
if (contentsBySource[source] != map.sourcesContent[i]) {
throw new Error("Mismatched sourcesContent for: " + source);
}
}
}
}
files.forEach(function (file, index) {
if (index !== 0) {
concatenated.add("\n");
}

wrapSourceMap(map).eachMapping(function(mapping) {
if (!mapping.originalLine || !mapping.originalColumn || !mapping.source || mapping.source.match(/(\/|^)@traceur/))
return;

generated.addMapping({
generated: {
line: offset + mapping.generatedLine,
column: mapping.generatedColumn
},
original: {
line: mapping.originalLine,
column: mapping.originalColumn
},
source: mapping.source,
name: mapping.name
});
});
});

// normalize source paths and inject sourcesContent if necessary
var normalized = JSON.parse(JSON.stringify(generated));

if (sourceMapContents) {
normalized.sourcesContent = normalized.sources.map(function(source) {
if (contentsBySource[source])
return contentsBySource[source];

try {
return fs.readFileSync(path.resolve(basePath, source)).toString();
}
catch (e) {
return "";
var node;
var map = file.map;
if (map) {
if (typeof map.toJSON === "function") {
map = map.toJSON();
}
});
}

normalized.sources = normalized.sources.map(function(source) {
if (isFileURL(source))
source = fromFileURL(source);
node = SourceNode.fromStringWithSourceMap(
file.code,
new SourceMapConsumer(map),
path.relative(
path.dirname(outFile + ".map"),
path.dirname(".")
)
);
} else {
node = new SourceNode(null, null, null, file.code);
}

return path.relative(outPath, path.resolve(basePath, source)).replace(/\\/g, '/');
concatenated.add(node);
});

return JSON.stringify(normalized);
return concatenated;
};

0 comments on commit a49584b

Please sign in to comment.