Rewrite source map composition for CSS files

The previous implementation had a run time quadratic in the number of CSS files with an associated source map. This new implementation has a linear run time because it iterates only once through all mappings of each CSS file.
This commit is contained in:
Christian Klaussner
2016-10-06 18:20:00 +02:00
parent 232ab2d209
commit 00fc89b373

View File

@@ -119,26 +119,73 @@ var mergeCss = Profile("mergeCss", function (css) {
var newMap;
// Compose the concatenated file's source map with source maps from the
// previous build step if necessary.
Profile.time("composing source maps", function () {
// If any input files had source maps, apply them.
// Ex.: less -> css source map should be composed with css -> css source map
newMap = sourcemap.SourceMapGenerator.fromSourceMap(
new sourcemap.SourceMapConsumer(stringifiedCss.map));
var concatConsumer;
Object.keys(originals).forEach(function (name) {
newMap = new sourcemap.SourceMapGenerator();
concatConsumer = new sourcemap.SourceMapConsumer(stringifiedCss.map);
// Create a dictionary of source map consumers for fast access
var consumers = Object.keys(originals).reduce(function (consumers, name) {
var file = originals[name];
if (! file.getSourceMap())
return;
try {
newMap.applySourceMap(
new sourcemap.SourceMapConsumer(file.getSourceMap()), name);
} catch (err) {
// If we can't apply the source map, silently drop it.
//
// XXX This is here because there are some less files that
// produce source maps that throw when consumed. We should
// figure out exactly why and fix it, but this will do for now.
var sourceMap = file.getSourceMap();
if (sourceMap) {
try {
consumers[name] = new sourcemap.SourceMapConsumer(sourceMap);
} catch (err) {
// If we can't apply the source map, silently drop it.
//
// XXX This is here because there are some less files that
// produce source maps that throw when consumed. We should
// figure out exactly why and fix it, but this will do for now.
}
}
return consumers;
}, Object.create(null));
// Find mappings from the concatenated file back to the original files
concatConsumer.eachMapping(function (mapping) {
var consumer = consumers[mapping.source];
var source;
// If there is a source map for the original file, e.g., if it has been
// compiled from Less to CSS, find the source location in the original's
// original file. Otherwise, use the mapping of the concatenated file's
// source map.
var original = {
line: mapping.originalLine,
column: mapping.originalColumn
};
if (consumer) {
original = consumer.originalPositionFor(original);
source = original.source;
} else {
source = mapping.source;
}
// Add a new mapping to the final source map
newMap.addMapping({
generated: {
line: mapping.generatedLine,
column: mapping.generatedColumn
},
original: {
line: original.line,
column: original.column
},
source: source
});
// Set the correct content for the mapping's source
newMap.setSourceContent(
source,
(consumer || concatConsumer).sourceContentFor(source)
);
});
});