bare-cov
Version:
Generate coverage reports using Bare and Node.js
119 lines (100 loc) • 3.77 kB
JavaScript
// Based on https://github.com/istanbuljs/istanbuljs/blob/main/packages/istanbul-lib-coverage/lib/file-coverage.js
const isLineColumn = (o) => typeof o?.line === 'number' && typeof o?.column === 'number'
const isLoc = (o) => isLineColumn(o?.start) && isLineColumn(o.end)
const getLoc = (o) => (isLoc(o) ? o : isLoc(o.loc) ? o.loc : null)
function findNearestContainer(coverageRecord, targetRecordMap) {
const loc = getLoc(coverageRecord)
if (!loc) return null
let container = { nearest: null, distance: null, key: null }
for (const [key, [, targetCoverageRecord]] of Object.entries(targetRecordMap)) {
const tLoc = getLoc(targetCoverageRecord)
if (!tLoc) continue
const distance = [
loc.start.line - tLoc.start.line,
loc.start.column - tLoc.start.column,
tLoc.end.line - loc.end.line,
tLoc.end.column - loc.end.column
]
if (
distance[0] < 0 ||
distance[2] < 0 ||
(distance[0] === 0 && distance[1] < 0) ||
(distance[2] === 0 && distance[3] < 0)
)
continue
if (container.nearest === null) {
container = { nearest: targetCoverageRecord, distance, key }
continue
}
const closerBefore =
distance[0] < container.distance[0] ||
(distance[0] === 0 && distance[1] < container.distance[1])
const closerAfter =
distance[2] < container.distance[2] ||
(distance[2] === 0 && distance[3] < container.distance[3])
if (closerBefore || closerAfter) container = { nearest: targetCoverageRecord, distance, key }
}
return container.key
}
function addContainedHits(hits, coverageRecord, sourceRecord) {
const container = findNearestContainer(coverageRecord, sourceRecord)
return container ? addHits(hits, sourceRecord[container][0]) : hits
}
function addHits(a, b) {
if (typeof a === 'number' && typeof b === 'number') return a + b
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length >= b.length) return a.map((hits, i) => hits + (b[i] || 0))
return b.map((hits, i) => hits + (a[i] || 0))
}
return null
}
function toHitMap(hits, map, toKey) {
return Object.entries(hits).reduce((acc, [id, value]) => {
const coverageRecord = map[id]
if (coverageRecord) acc[toKey(coverageRecord)] = [value, coverageRecord]
return acc
}, {})
}
function mergeHitMaps(a, b) {
const mergedRecords = {}
for (const [key, [hits, record]] of Object.entries(a)) {
const matchingValue = b[key]
const newHits = matchingValue
? addHits(hits, matchingValue[0])
: addContainedHits(hits, record, b)
mergedRecords[key] = [newHits, record]
}
for (const [key, [hits, record]] of Object.entries(b)) {
if (mergedRecords[key]) continue
const newHits = addContainedHits(hits, record, a)
mergedRecords[key] = [newHits, record]
}
return Object.values(mergedRecords).reduce(
(acc, [hits, record], index) => {
acc.hits[index] = hits
acc.map[index] = record
return acc
},
{ hits: {}, map: {} }
)
}
function mergeCoverages(a, b) {
const merged = { ...a }
const toKey = ({ start, end }) => `${start.line}|${start.column}|${end.line}|${end.column}`
;({ hits: merged.s, map: merged.statementMap } = mergeHitMaps(
toHitMap(a.s, a.statementMap, toKey),
toHitMap(b.s, b.statementMap, toKey)
))
const toKeyFn = ({ loc }) => toKey(loc)
;({ hits: merged.f, map: merged.fnMap } = mergeHitMaps(
toHitMap(a.f, a.fnMap, toKeyFn),
toHitMap(b.f, b.fnMap, toKeyFn)
))
const toKeyBranch = ({ locations }) => toKey(locations[0])
;({ hits: merged.b, map: merged.branchMap } = mergeHitMaps(
toHitMap(a.b, a.branchMap, toKeyBranch),
toHitMap(b.b, b.branchMap, toKeyBranch)
))
return merged
}
module.exports = mergeCoverages