UNPKG

@plugjs/cov8

Version:

V8 Coverage Plugin for the PlugJS Build System ==============================================

237 lines (235 loc) 8.83 kB
"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