182 lines
5.2 KiB
JavaScript
182 lines
5.2 KiB
JavaScript
/*
|
|
Copyright 2015, Yahoo Inc.
|
|
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
|
|
*/
|
|
"use strict";
|
|
|
|
var debug = require('debug')('istanbuljs'),
|
|
pathutils = require('./pathutils'),
|
|
libCoverage = require('istanbul-lib-coverage'),
|
|
MappedCoverage = require('./mapped').MappedCoverage;
|
|
|
|
function isInvalidPosition (pos) {
|
|
return !pos || typeof pos.line !== "number" || typeof pos.column !== "number" || pos.line < 0 || pos.column < 0;
|
|
}
|
|
|
|
/**
|
|
* determines the original position for a given location
|
|
* @param {SourceMapConsumer} sourceMap the source map
|
|
* @param {Object} location the original location Object
|
|
* @returns {Object} the remapped location Object
|
|
*/
|
|
function getMapping(sourceMap, location, origFile) {
|
|
|
|
if (!location) {
|
|
return null;
|
|
}
|
|
|
|
if (isInvalidPosition(location.start) || isInvalidPosition(location.end)) {
|
|
return null;
|
|
}
|
|
|
|
var start = sourceMap.originalPositionFor(location.start),
|
|
end = sourceMap.originalPositionFor(location.end);
|
|
|
|
/* istanbul ignore if: edge case too hard to test for */
|
|
if (!(start && end)) {
|
|
return null;
|
|
}
|
|
if (!(start.source && end.source)) {
|
|
return null;
|
|
}
|
|
if (start.source !== end.source) {
|
|
return null;
|
|
}
|
|
|
|
/* istanbul ignore if: edge case too hard to test for */
|
|
if (start.line === null || start.column === null) {
|
|
return null;
|
|
}
|
|
/* istanbul ignore if: edge case too hard to test for */
|
|
if (end.line === null || end.column === null) {
|
|
return null;
|
|
}
|
|
|
|
if (start.line === end.line && start.column === end.column) {
|
|
end = sourceMap.originalPositionFor({
|
|
line: location.end.line,
|
|
column: location.end.column,
|
|
bias: 2
|
|
});
|
|
end.column = end.column - 1;
|
|
}
|
|
|
|
return {
|
|
source: pathutils.relativeTo(start.source, origFile),
|
|
loc: {
|
|
start: {
|
|
line: start.line,
|
|
column: start.column
|
|
},
|
|
end: {
|
|
line: end.line,
|
|
column: end.column
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function SourceMapTransformer(finder, opts) {
|
|
opts = opts || {};
|
|
this.finder = finder;
|
|
this.baseDir = opts.baseDir || process.cwd();
|
|
}
|
|
|
|
SourceMapTransformer.prototype.processFile = function (fc, sourceMap, coverageMapper) {
|
|
var changes = 0;
|
|
|
|
Object.keys(fc.statementMap).forEach(function (s) {
|
|
var loc = fc.statementMap[s],
|
|
hits = fc.s[s],
|
|
mapping = getMapping(sourceMap, loc, fc.path),
|
|
mappedCoverage;
|
|
|
|
if (mapping) {
|
|
changes += 1;
|
|
mappedCoverage = coverageMapper(mapping.source);
|
|
mappedCoverage.addStatement(mapping.loc, hits);
|
|
}
|
|
});
|
|
|
|
Object.keys(fc.fnMap).forEach(function (f) {
|
|
var fnMeta = fc.fnMap[f],
|
|
hits = fc.f[f],
|
|
mapping = getMapping(sourceMap, fnMeta.decl, fc.path),
|
|
spanMapping = getMapping(sourceMap, fnMeta.loc, fc.path),
|
|
mappedCoverage;
|
|
|
|
if (mapping && spanMapping && mapping.source === spanMapping.source) {
|
|
changes += 1;
|
|
mappedCoverage = coverageMapper(mapping.source);
|
|
mappedCoverage.addFunction(fnMeta.name, mapping.loc, spanMapping.loc, hits);
|
|
}
|
|
});
|
|
|
|
Object.keys(fc.branchMap).forEach(function (b) {
|
|
var branchMeta = fc.branchMap[b],
|
|
source,
|
|
hits = fc.b[b],
|
|
mapping,
|
|
locs = [],
|
|
mappedHits = [],
|
|
mappedCoverage,
|
|
skip,
|
|
i;
|
|
for (i = 0; i < branchMeta.locations.length; i += 1) {
|
|
mapping = getMapping(sourceMap, branchMeta.locations[i], fc.path);
|
|
if (mapping) {
|
|
if (!source) {
|
|
source = mapping.source;
|
|
}
|
|
if (mapping.source !== source) {
|
|
skip = true;
|
|
}
|
|
locs.push(mapping.loc);
|
|
mappedHits.push(hits[i]);
|
|
}
|
|
}
|
|
if (!skip && locs.length > 0) {
|
|
changes += 1;
|
|
mappedCoverage = coverageMapper(source);
|
|
mappedCoverage.addBranch(branchMeta.type, locs[0] /* XXX */, locs, mappedHits);
|
|
}
|
|
});
|
|
|
|
return changes > 0;
|
|
};
|
|
|
|
SourceMapTransformer.prototype.transform = function (coverageMap) {
|
|
var that = this,
|
|
finder = this.finder,
|
|
output = {},
|
|
getMappedCoverage = function (file) {
|
|
if (!output[file]) {
|
|
output[file] = new MappedCoverage(file);
|
|
}
|
|
return output[file];
|
|
};
|
|
|
|
coverageMap.files().forEach(function (file) {
|
|
var fc = coverageMap.fileCoverageFor(file),
|
|
sourceMap = finder(file),
|
|
changed;
|
|
|
|
if (!sourceMap) {
|
|
output[file] = fc;
|
|
return;
|
|
}
|
|
|
|
changed = that.processFile(fc, sourceMap, getMappedCoverage);
|
|
if (!changed) {
|
|
debug('File [' + file + '] ignored, nothing could be mapped');
|
|
}
|
|
});
|
|
return libCoverage.createCoverageMap(output);
|
|
};
|
|
|
|
module.exports = {
|
|
create: function (finder, opts) {
|
|
return new SourceMapTransformer(finder, opts);
|
|
}
|
|
};
|