import libCoverage from 'istanbul-lib-coverage'; import libReport from 'istanbul-lib-report'; import reports from 'istanbul-reports'; import { glob } from "../lib/builderator.js"; import fs from "fs"; import v8toIstanbul from 'v8-to-istanbul'; import assert from "assert"; import url from "url"; import normalize from "normalize-path"; export const description = "Takes the output of a nv8 coverage directory and generates a report."; export const argument = ["", "coverage directory"]; export const main = async (coverage_dir) => { const covdir = normalize(coverage_dir); const covpattern = `${covdir}/**/*.json`; console.log(`Searching ${covpattern} for coverage files...`); const covfiles = glob(covpattern); console.log(`Found ${covfiles.length} .json files in ${covdir}`); const coverage = {}; const excludes = [ "node_modules", "secrets", "/net" ]; for(const fname of covfiles) { const data = JSON.parse(fs.readFileSync(fname)); // test removing just node modules data.result = data.result.filter(x => { if(x.url) { // we need to do surgery on the URL because node is bad at them let pathname = url.parse(x.url).pathname; // fix the URL and turn it into a file name if(!pathname) { return false; } else if(pathname.startsWith("/C:")) { // why does url not parse windows paths right? // remove the leading / so it's parsed correctly x.url = pathname.slice(1); } else { x.url = pathname; } const excluded = excludes.filter(e => x.url.includes(e)); return excluded.length === 0 && fs.existsSync(x.url); } else { return false; } }); // looks good, save it if(data.result.length > 0) { coverage[fname] = data; } } const coverageMap = libCoverage.createCoverageMap(); console.log("After filtering, found count is:", Object.entries(coverage).length); for(const [fname, data] of Object.entries(coverage)) { for(const entry of data.result) { let converter; const pathname = url.parse(entry.url).pathname assert(fs.existsSync(pathname), `coverage entry in ${fname} contains ${entry.url} that doesn't exist but should`); converter = v8toIstanbul(pathname, 0, {source: entry.source}, path => { const excluded = excludes.filter(e => path.includes(e)); return excluded.length > 0; }); try { await converter.load(); converter.applyCoverage(entry.functions); coverageMap.merge(converter.toIstanbul()); } catch(error) { console.error(error, "load", entry.url); } } } const watermarks = undefined; // used in check coverage ignored here const context = libReport.createContext({ dir: "coverage", watermarks, coverageMap }); ["text","html"].forEach(format => { reports.create(format, { skipEmpty: false, skipFull: true, maxCols: 100 }).execute(context); }); process.exit(0); }