UNPKG

@plugjs/cov8

Version:

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

237 lines (235 loc) 8.94 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); // report.ts var report_exports = {}; __export(report_exports, { COVERAGE_IGNORED: () => COVERAGE_IGNORED, COVERAGE_SKIPPED: () => COVERAGE_SKIPPED, coverageReport: () => coverageReport }); module.exports = __toCommonJS(report_exports); var import_node_url = require("node:url"); var import_parser = require("@babel/parser"); var import_types = require("@babel/types"); var import_fs = require("@plugjs/plug/fs"); var import_logging = require("@plugjs/plug/logging"); var COVERAGE_SKIPPED = -2; var COVERAGE_IGNORED = -1; var ignoreRegexp = /^\s+(coverage|istanbul)\s+ignore\s+(test|if|else|try|catch|finally|next|prev|file)(\s|$)/g; async function coverageReport(analyser, sourceFiles, log) { const results = {}; const nodes = { coveredNodes: 0, missingNodes: 0, ignoredNodes: 0, totalNodes: 0, coverage: 0 }; for (const file of sourceFiles) { const url = (0, import_node_url.pathToFileURL)(file).toString(); const code = await (0, import_fs.readFile)(file, "utf-8"); let tree; try { tree = (0, import_parser.parse)(code, { allowImportExportEverywhere: true, allowAwaitOutsideFunction: true, allowReturnOutsideFunction: true, allowSuperOutsideMethod: true, allowUndeclaredExports: true, attachComment: true, errorRecovery: false, sourceType: "unambiguous", sourceFilename: file, startLine: 1, startColumn: 0, plugins: ["typescript"], strictMode: false, ranges: false, tokens: false, createParenthesizedExpressions: true }); } catch (error) { log.fail(`Error parsing ${(0, import_logging.$p)(file)}`, error); } const codeCoverage = new Array(code.length).fill(0); const nodeCoverage = { coveredNodes: 0, missingNodes: 0, ignoredNodes: 0, totalNodes: 0, coverage: 0 }; const setCodeCoverage = (node, coverage, recursive) => { if (!node) return; if (Array.isArray(node)) { for (const n of node) setCodeCoverage(n, coverage, recursive); return; } if (node.start != null && node.end != null) { for (let i = node.start; i < node.end; i++) { codeCoverage[i] = coverage; } } if (coverage == COVERAGE_IGNORED) { nodeCoverage.ignoredNodes++; } else if (coverage === 0) { nodeCoverage.missingNodes++; } else if (coverage > 0) { nodeCoverage.coveredNodes++; } if (!recursive) return; const keys = import_types.VISITOR_KEYS[node.type] || /* coverage ignore next */ []; for (const key of keys) { const value = node[key]; if (Array.isArray(value)) { for (const child of value) { setCodeCoverage(child, coverage, true); } } else if (value) { setCodeCoverage(value, coverage, true); } } }; const visitChildren = (node, depth) => { const keys = import_types.VISITOR_KEYS[node.type] || /* coverage ignore next */ []; for (const key of keys) { const children = node[key]; if (Array.isArray(children)) { for (const child of children) { if (child) visitNode(child, depth + 1); } } else if (children) { visitNode(children, depth + 1); } } }; const maybeIgnoreNode = (condition, node, depth) => { if (condition) { setCodeCoverage(node, COVERAGE_IGNORED, true); } else if (node) { visitNode(node, depth); } }; const visitNode = (node, depth) => { log.trace("-".padStart(depth * 2 + 1, " "), node.type, `${node.loc?.start.line}:${node.loc?.start.column}`); if ((0, import_types.isFile)(node)) return visitChildren(node, depth); if ((0, import_types.isProgram)(node)) return visitChildren(node, depth); const ignores = []; for (const comment of node.leadingComments || []) { for (const match of comment.value.matchAll(ignoreRegexp)) { if (match[2] !== "prev") ignores.push(match[2]); } } for (const comment of node.trailingComments || []) { for (const match of comment.value.matchAll(ignoreRegexp)) { if (match[2] === "prev") ignores.push(match[2]); } } if (ignores.includes("next")) return setCodeCoverage(node, COVERAGE_IGNORED, true); if (ignores.includes("prev")) return setCodeCoverage(node, COVERAGE_IGNORED, true); if ((0, import_types.isTypeScript)(node)) { if ((0, import_types.isTSDeclareMethod)(node)) return setCodeCoverage(node, COVERAGE_SKIPPED, true); if ((0, import_types.isTSTypeReference)(node)) return setCodeCoverage(node, COVERAGE_SKIPPED, true); if ((0, import_types.isDeclaration)(node)) return setCodeCoverage(node, COVERAGE_SKIPPED, true); setCodeCoverage(node, COVERAGE_SKIPPED, false); return visitChildren(node, depth); } if ((0, import_types.isExportDeclaration)(node) && node.exportKind === "type") { return setCodeCoverage(node, COVERAGE_SKIPPED, true); } if ((0, import_types.isImportDeclaration)(node) && node.importKind === "type") { return setCodeCoverage(node, COVERAGE_SKIPPED, true); } let coverage = 0; if (node.loc) { const { line, column } = node.loc.start; const c = analyser.coverage(url, line, column); if (c == null) { log.warn(`No coverage for ${node.type} at ${(0, import_logging.$p)(file)}:${line}:${column}`); } else { coverage = c; } } setCodeCoverage(node, coverage, false); if ((0, import_types.isIfStatement)(node)) { maybeIgnoreNode(ignores.includes("test"), node.test, depth + 1); maybeIgnoreNode(ignores.includes("if"), node.consequent, depth + 1); maybeIgnoreNode(ignores.includes("else"), node.alternate, depth + 1); return; } if ((0, import_types.isTryStatement)(node)) { maybeIgnoreNode(ignores.includes("try"), node.block, depth + 1); maybeIgnoreNode(ignores.includes("catch"), node.handler, depth + 1); maybeIgnoreNode(ignores.includes("finally"), node.finalizer, depth + 1); return; } visitChildren(node, depth); }; codeCoverage.fill(COVERAGE_SKIPPED); setCodeCoverage(tree.program.directives, 0, true); setCodeCoverage(tree.program.body, 0, true); nodeCoverage.coveredNodes = 0; nodeCoverage.missingNodes = 0; nodeCoverage.ignoredNodes = 0; let ignoreFileCoverage = false; for (const comment of tree.program.body[0]?.leadingComments || []) { for (const match of comment.value.matchAll(ignoreRegexp)) { if (match[2] === "file") { ignoreFileCoverage = true; break; } } if (ignoreFileCoverage) break; } if (ignoreFileCoverage) { setCodeCoverage(tree.program, COVERAGE_IGNORED, true); } else { visitChildren(tree.program, -1); } setCodeCoverage(tree.comments, COVERAGE_SKIPPED, false); updateNodeCoverageResult(nodeCoverage); nodes.coveredNodes += nodeCoverage.coveredNodes; nodes.missingNodes += nodeCoverage.missingNodes; nodes.ignoredNodes += nodeCoverage.ignoredNodes; nodes.totalNodes += nodeCoverage.totalNodes; results[file] = { code, codeCoverage, nodeCoverage }; } updateNodeCoverageResult(nodes); return { results, nodes }; } function updateNodeCoverageResult(result) { const { coveredNodes, missingNodes, ignoredNodes } = result; const totalNodes = result.totalNodes = coveredNodes + missingNodes + ignoredNodes; if (totalNodes === 0) { result.coverage = null; } else if (totalNodes === ignoredNodes) { result.coverage = null; } else { result.coverage = Math.floor(100 * coveredNodes / (totalNodes - ignoredNodes)); } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { COVERAGE_IGNORED, COVERAGE_SKIPPED, coverageReport }); //# sourceMappingURL=report.cjs.map