UNPKG

@plugjs/cov8

Version:

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

124 lines (123 loc) 4.97 kB
// coverage.ts import { sep } from "node:path"; import { html, initFunction } from "@plugjs/cov8-html"; import { assert } from "@plugjs/plug/asserts"; import { Files } from "@plugjs/plug/files"; import { $gry, $ms, $p, $plur, $red, $ylw, ERROR, NOTICE, WARN } from "@plugjs/plug/logging"; import { resolveAbsolutePath } from "@plugjs/plug/paths"; import { walk } from "@plugjs/plug/utils"; import { createAnalyser } from "./analysis.mjs"; import { coverageReport } from "./report.mjs"; var Coverage = class { constructor(_coverageDir, _options = {}) { this._coverageDir = _coverageDir; this._options = _options; } async pipe(files, context) { const coverageDir = context.resolve(this._coverageDir); const coverageFiles = []; for await (const file of walk(coverageDir, ["coverage-*.json"])) { coverageFiles.push(resolveAbsolutePath(coverageDir, file)); } assert(coverageFiles.length > 0, `No coverage files found in ${$p(coverageDir)}`); const sourceFiles = [...files.absolutePaths()]; const ms1 = Date.now(); const analyser = await createAnalyser( sourceFiles, coverageFiles, this._options.sourceMapBias || "least_upper_bound", context.log ); context.log.info("Parsed", coverageFiles.length, "coverage files", $ms(Date.now() - ms1)); const ms2 = Date.now(); const report = await coverageReport(analyser, sourceFiles, context.log); context.log.info("Analysed", sourceFiles.length, "source files", $ms(Date.now() - ms2)); analyser.destroy(); const { minimumCoverage = 50, minimumFileCoverage = minimumCoverage, optimalCoverage = Math.round((100 + minimumCoverage) / 2), optimalFileCoverage = Math.round((100 + minimumFileCoverage) / 2) } = this._options; let max = 0; for (const file in report) { if (file.length > max) max = file.length; } let maxLength = 0; for (const file in report.results) { if (file.length > maxLength) maxLength = file.length; } let fileErrors = 0; let fileWarnings = 0; const _report = context.log.report("Coverage report"); for (const [_file, result] of Object.entries(report.results)) { const { coverage } = result.nodeCoverage; const file = _file; if (coverage == null) { _report.annotate(NOTICE, file, "n/a"); } else if (coverage < minimumFileCoverage) { _report.annotate(ERROR, file, `${coverage} %`); fileErrors++; } else if (coverage < optimalFileCoverage) { _report.annotate(WARN, file, `${coverage} %`); fileWarnings++; } else { _report.annotate(NOTICE, file, `${coverage} %`); } } if (report.nodes.coverage == null) { const message = "No coverage data collected"; _report.add({ level: WARN, message }); } else if (report.nodes.coverage < minimumCoverage) { const message = `${$red(`${report.nodes.coverage}%`)} does not meet minimum coverage ${$gry(`(${minimumCoverage}%)`)}`; _report.add({ level: ERROR, message }); } else if (report.nodes.coverage < optimalCoverage) { const message = `${$ylw(`${report.nodes.coverage}%`)} does not meet optimal coverage ${$gry(`(${optimalCoverage}%)`)}`; _report.add({ level: WARN, message }); } if (fileErrors) { const f = $plur(fileErrors, "file does", "files do"); const message = `${f} not meet minimum file coverage ${$gry(`(${minimumFileCoverage}%)`)}`; _report.add({ level: ERROR, message }); } if (fileWarnings) { const f = $plur(fileWarnings, "file does", "files do"); const message = `${f} not meet optimal file coverage ${$gry(`(${optimalFileCoverage}%)`)}`; _report.add({ level: WARN, message }); } if (this._options.reportDir == null) return _report.done(false); const reportDir = context.resolve(this._options.reportDir); const builder = Files.builder(reportDir); const date = (/* @__PURE__ */ new Date()).toISOString(); const thresholds = { minimumCoverage, minimumFileCoverage, optimalCoverage, optimalFileCoverage }; await builder.write("report.json", JSON.stringify({ ...report, thresholds, date })); await builder.write("index.html", html); const results = {}; for (const [rel, abs] of files.pathMappings()) { results[rel] = report.results[abs]; } const tree = {}; for (const relative of Object.keys(results)) { const directories = relative.split(sep); const file = directories.pop(); let node = tree; for (const dir of directories) { node = node[dir] = node[dir] || {}; } node[file] = relative; } const jsonp = JSON.stringify({ ...report, results, thresholds, tree, date }); await builder.write("report.js", `${initFunction}(${jsonp});`); _report.done(false); return builder.build(); } }; export { Coverage }; //# sourceMappingURL=coverage.mjs.map