@plugjs/cov8
Version:
V8 Coverage Plugin for the PlugJS Build System ==============================================
237 lines (235 loc) • 8.83 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// analysis.ts
var analysis_exports = {};
__export(analysis_exports, {
CombiningCoverageAnalyser: () => CombiningCoverageAnalyser,
SourcesCoverageAnalyser: () => SourcesCoverageAnalyser,
createAnalyser: () => createAnalyser
});
module.exports = __toCommonJS(analysis_exports);
var import_node_url = require("node:url");
var import_asserts = require("@plugjs/plug/asserts");
var import_fs = require("@plugjs/plug/fs");
var import_logging = require("@plugjs/plug/logging");
var import_source_map = require("source-map");
var CoverageAnalyserImpl = class {
constructor(_log) {
this._log = _log;
}
};
var CoverageResultAnalyser = class extends CoverageAnalyserImpl {
constructor(log, _result) {
super(log);
this._result = _result;
const _coverage = [];
for (const coveredFunction of _result.functions) {
for (const range of coveredFunction.ranges) {
for (let i = range.startOffset; i < range.endOffset; i++) {
_coverage[i] = range.count;
}
}
}
this._coverage = _coverage;
}
/** Number of passes at each character in the result */
_coverage;
/** Internal private field for init/_lineLengths getter */
_lineLengths;
async init() {
const filename = (0, import_node_url.fileURLToPath)(this._result.url);
const source = await (0, import_fs.readFile)(filename, "utf-8");
this._lineLengths = source.split("\n").map((line) => line.length);
return this;
}
destroy() {
}
/** Return the number of coverage passes for the given location */
coverage(source, line, column) {
(0, import_asserts.assert)(this._lineLengths, "Analyser not initialized");
(0, import_asserts.assert)(source === this._result.url, `Wrong source ${source} (should be ${this._result.url})`);
const { _lineLengths, _coverage } = this;
let offset = 0;
for (let l = line - 2; l >= 0; l--) offset += _lineLengths[l] + 1;
return _coverage[offset + column] || 0;
}
};
var CoverageSitemapAnalyser = class extends CoverageResultAnalyser {
constructor(log, result, _sourceMapCache, _sourceMapBias) {
super(log, result);
this._sourceMapCache = _sourceMapCache;
this._sourceMapBias = _sourceMapBias;
this._lineLengths = _sourceMapCache.lineLengths;
}
_preciseMappings = /* @__PURE__ */ new Map();
_sourceMap;
_key(source, line, column) {
return `${line}:${column}:${source}`;
}
async init() {
const sourceMap = this._sourceMapCache.data;
(0, import_asserts.assert)(sourceMap, "Missing source map data from cache");
this._sourceMap = await new import_source_map.SourceMapConsumer(sourceMap);
if (this._sourceMapBias === "none") {
this._sourceMap.eachMapping((m) => {
const location = { line: m.generatedLine, column: m.generatedColumn };
const key = this._key(m.source, m.originalLine, m.originalColumn);
this._preciseMappings.set(key, location);
});
}
return this;
}
destroy() {
this._sourceMap?.destroy();
}
coverage(source, line, column) {
(0, import_asserts.assert)(this._sourceMap, "Analyser not initialized");
if (this._sourceMapBias === "none") {
const key = this._key(source, line, column);
const location = this._preciseMappings.get(key);
if (!location) {
this._log.debug(`No precise mapping for ${source}:${line}:${column}`);
return 0;
} else {
return super.coverage(this._result.url, location.line, location.column);
}
}
const bias = mapBias(this._sourceMapBias);
const generated = this._sourceMap.generatedPositionFor({ source, line, column, bias });
if (!generated) {
this._log.debug(`No position generated for ${source}:${line}:${column}`);
return 0;
}
if (generated.line == null) {
this._log.debug(`No line generated for ${source}:${line}:${column}`);
return 0;
}
if (generated.column == null) {
this._log.debug(`No column generated for ${source}:${line}:${column}`);
return 0;
}
return super.coverage(this._result.url, generated.line, generated.column);
}
};
function mapBias(bias) {
if (bias === "greatest_lower_bound") return import_source_map.SourceMapConsumer.GREATEST_LOWER_BOUND;
if (bias === "least_upper_bound") return import_source_map.SourceMapConsumer.LEAST_UPPER_BOUND;
return void 0;
}
function combineCoverage(analysers, source, line, column) {
let coverage = 0;
if (!analysers) return coverage;
for (const analyser of analysers) {
coverage += analyser.coverage(source, line, column);
}
return coverage;
}
var SourcesCoverageAnalyser = class extends CoverageAnalyserImpl {
constructor(log, _filename) {
super(log);
this._filename = _filename;
}
_mappings = /* @__PURE__ */ new Map();
hasMappings() {
return this._mappings.size > 0;
}
add(source, analyser) {
const analysers = this._mappings.get(source) || /* @__PURE__ */ new Set();
analysers.add(analyser);
this._mappings.set(source, analysers);
}
async init() {
this._log.debug("SourcesCoverageAnalyser", (0, import_logging.$p)(this._filename), (0, import_logging.$gry)(`(${this._mappings.size} mappings)`));
for (const analysers of this._mappings.values()) {
for (const analyser of analysers) {
await analyser.init();
}
}
return this;
}
destroy() {
for (const analysers of this._mappings.values()) {
for (const analyser of analysers) {
analyser.destroy();
}
}
}
coverage(source, line, column) {
const analysers = this._mappings.get(source);
return combineCoverage(analysers, source, line, column);
}
};
var CombiningCoverageAnalyser = class extends CoverageAnalyserImpl {
_analysers = /* @__PURE__ */ new Set();
add(analyser) {
this._analysers.add(analyser);
}
async init() {
this._log.debug("CombiningCoverageAnalyser", (0, import_logging.$gry)(`(${this._analysers.size} analysers)`));
this._log.enter();
try {
for (const analyser of this._analysers) await analyser.init();
return this;
} finally {
this._log.leave();
}
}
destroy() {
for (const analyser of this._analysers) analyser.destroy();
}
coverage(source, line, column) {
return combineCoverage(this._analysers, source, line, column);
}
};
async function createAnalyser(sourceFiles, coverageFiles, sourceMapBias, log) {
const urls = sourceFiles.map((path) => (0, import_node_url.pathToFileURL)(path).toString());
const analyser = new CombiningCoverageAnalyser(log);
for await (const coverageFile of coverageFiles) {
const coverageFileAnalyser = new SourcesCoverageAnalyser(log, coverageFile);
log.info("Parsing coverage file", (0, import_logging.$p)(coverageFile));
const contents = await (0, import_fs.readFile)(coverageFile, "utf-8");
const coverage = JSON.parse(contents);
for (const result of coverage.result) {
if (!result.url.startsWith("node:")) {
log.debug("Found coverage data for", result.url);
}
const mapping = coverage["source-map-cache"]?.[result.url];
if (mapping) {
log.debug("Found source mapping for", result.url, mapping.data);
const matches = urls.filter((url) => mapping.data?.sources.includes(url));
if (matches.length) {
log.debug("Found source mapping matches", matches);
const sourceAnalyser = new CoverageSitemapAnalyser(log, result, mapping, sourceMapBias);
for (const match of matches) coverageFileAnalyser.add(match, sourceAnalyser);
}
} else if (urls.includes(result.url)) {
coverageFileAnalyser.add(result.url, new CoverageResultAnalyser(log, result));
}
}
if (coverageFileAnalyser.hasMappings()) analyser.add(coverageFileAnalyser);
}
return await analyser.init();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CombiningCoverageAnalyser,
SourcesCoverageAnalyser,
createAnalyser
});
//# sourceMappingURL=analysis.cjs.map