UNPKG

might-cli

Version:

A no-code solution for performing frontend tests

130 lines (129 loc) 5.17 kB
import v8toIstanbul from 'v8-to-istanbul'; import { createContext } from 'istanbul-lib-report'; import libCoverage from 'istanbul-lib-coverage'; import reports from 'istanbul-reports'; import { join } from 'path'; import fs from 'fs-extra'; import { SourceMapConsumer } from 'source-map'; import convert from 'convert-source-map'; import nanomatch from 'nanomatch'; export async function coverage(coverageCollection, meta, dir, exclude) { const mainMap = libCoverage.createCoverageMap(); const processed = {}; for (const entry of coverageCollection) { const entryPath = new URL(entry.url).pathname; if (!entryPath.length || nanomatch.any(entryPath.replace('\\', ''), exclude, undefined)) continue; if (!processed[entryPath]) { const sourcemap = await getSourcemap(entry); if (sourcemap) { const consumer = await SourceMapConsumer.with(sourcemap, undefined, c => c); await Promise.all(consumer.sources .map(async (path, id) => { var _a; let relative = path.replace(/\\/g, ''); if (path.includes('://')) relative = path.substring(path.indexOf('://') + 3); relative = relative.replace(new RegExp(process.env.PWD, 'g'), ''); if (relative.startsWith(`/${meta.name}/`)) relative = relative.replace(`/${meta.name}`, ''); else if (relative.startsWith(`${meta.name}/`)) relative = relative.replace(meta.name, ''); if (!relative.startsWith('/')) relative = '/' + relative; if (!nanomatch.any(relative, exclude, undefined)) { try { await fs.outputFile(join(dir, relative), (_a = sourcemap === null || sourcemap === void 0 ? void 0 : sourcemap.sourcesContent) === null || _a === void 0 ? void 0 : _a[id]); } catch (err) { } } })); } processed[entryPath] = v8toIstanbul('', 0, { sourceMap: sourcemap ? { sourcemap } : undefined, source: entry.source }); await processed[entryPath].load(); } const converter = processed[entryPath]; converter.applyCoverage(entry.functions); const data = converter.toIstanbul(); Object.entries(data).forEach(([key, file]) => { key = key .replace(/\\/g, '') .replace(new RegExp(process.env.PWD, 'g'), ''); if (key.startsWith(`/${meta.name}/`)) key = key.replace(`/${meta.name}`, ''); else if (key.startsWith(`${meta.name}/`)) key = key.replace(meta.name, ''); if (!key.startsWith('/')) key = '/' + key; if (nanomatch.any(key, exclude, undefined)) return; file.path = join(dir, key); try { const exists = mainMap.fileCoverageFor(file.path); mergeMappings(file, exists); exists.merge(file); } catch { mainMap.addFileCoverage(file); } }); } const context = createContext({ dir, defaultSummarizer: 'nested', coverageMap: mainMap }); try { reports.create('json').execute(context); reports.create('clover').execute(context); reports.create('lcov').execute(context); } catch (e) { } return [Math.floor(mainMap.getCoverageSummary().lines.pct), mainMap.files().map(file => { const coverage = Math.floor(mainMap.fileCoverageFor(file).toSummary().lines.pct); const uncoveredLines = mainMap.fileCoverageFor(file).getUncoveredLines(); return { name: file.replace(dir, ''), coverage, uncoveredLines }; })]; } function mergeMappings(file, target) { Object.entries(file.fnMap).forEach(([key, mapping]) => { if (target.fnMap[key] === undefined) target.fnMap[key] = mapping; }); Object.entries(file.branchMap).forEach(([key, mapping]) => { if (target.branchMap[key] === undefined) target.branchMap[key] = mapping; }); } async function getSourcemap(entry) { var _a; let sourcemap; try { sourcemap = (_a = convert.fromSource(entry.source)) === null || _a === void 0 ? void 0 : _a.sourcemap; if (sourcemap) return sourcemap; } catch { } try { const fetch = (await import('node-fetch')).default; const response = await fetch(`${entry.url}.map`); if (response.status !== 200) throw new Error(); const buffer = Buffer.from(await response.arrayBuffer()); sourcemap = JSON.parse(buffer.toString('utf-8')); if (sourcemap) return sourcemap; } catch { } }