UNPKG

monocart-coverage-reports

Version:

A code coverage tool to generate native V8 reports or Istanbul reports.

316 lines (252 loc) 7.98 kB
const fs = require('fs'); const path = require('path'); const Util = require('../utils/util.js'); const { pathToFileURL, fileURLToPath } = require('url'); const { normalizeSourcePath, getSourcePathReplacer } = require('../utils/source-path.js'); // const EC = require('eight-colors'); const { minimatch, convertSourceMap } = require('../packages/monocart-coverage-vendor.js'); // ======================================================================================================== const resolveAllDirList = (input) => { const dirs = Util.toList(input, ','); if (!dirs.length) { return; } const dirList = dirs.filter((it) => fs.existsSync(it)); if (!dirList.length) { return; } return dirList; }; const resolveAllFilter = (input) => { // for function handler if (typeof input === 'function') { return input; } // for single minimatch pattern if (input && typeof input === 'string') { // string to multiple patterns "{...}" const obj = Util.strToObj(input); if (obj) { input = obj; } else { return (filePath) => { return minimatch(filePath, input); }; } } // for patterns if (input && typeof input === 'object') { const patterns = Object.keys(input); return (filePath) => { for (const pattern of patterns) { if (minimatch(filePath, pattern)) { return input[pattern]; } } // false if not matched }; } // default return () => true; }; const resolveAllOptions = (input) => { if (!input) { return; } let dir; let filter; let transformer; if (typeof input === 'string') { const obj = Util.strToObj(input); if (obj) { dir = obj.dir; filter = obj.filter; transformer = obj.transformer; } else { dir = input; } } else if (Array.isArray(input)) { dir = input; } else { dir = input.dir; filter = input.filter; transformer = input.transformer; } const dirList = resolveAllDirList(dir); if (!dirList) { return; } const fileFilter = resolveAllFilter(filter); const fileTransformer = typeof transformer === 'function' ? transformer : () => {}; return { dirList, fileFilter, fileTransformer }; }; const resolveFileType = (fileType, filePath) => { if (fileType === 'js' || fileType === 'css') { return fileType; } const extname = path.extname(filePath); if (['.css', '.scss', '.sass', '.less'].includes(extname)) { return 'css'; } return 'js'; }; const saveUntestedFileSource = async (entryFile, options) => { const { id, url, source, sourceMap } = entryFile; // console.log('-', entry.sourcePath); const { cachePath } = Util.getCacheFileInfo('source', id, options.cacheDir); if (fs.existsSync(cachePath)) { return; } // save source and sourceMap to separated json file const sourceData = { id, url, source, sourceMap }; // remove comments if not debug if (!Util.isDebug()) { sourceData.source = convertSourceMap.removeComments(source); } // console.log('save untested file', id, url); await Util.saveSourceCacheFile(sourceData, options); }; const getUntestedCoverageData = async (entryList, options, coverageType) => { // save all empty coverage, 20 - 5(empty) const dataId = Util.uid(15, 'empty'); const results = { id: dataId }; if (coverageType === 'istanbul') { results.type = 'istanbul'; results.data = {}; } else { results.type = 'v8'; results.data = []; } const emptyCoverageList = []; // save all empty source and sourcemap for (const entry of entryList) { // for raw report: source file // id, url, source, sourceMap await saveUntestedFileSource(entry, options); if (coverageType === 'istanbul') { // =============================================== const item = { path: fileURLToPath(entry.url), statementMap: {}, fnMap: {}, branchMap: {}, s: {}, f: {}, b: {} }; // object results.data[item.path] = item; emptyCoverageList.push(item); } else { // =============================================== Util.setEmptyV8Coverage(entry); const item = { ... entry }; delete item.source; delete item.sourceMap; // array results.data.push(item); // will be parsed to AST and converted to V8 coverage emptyCoverageList.push(entry); } } // for raw report: coverage file const { cachePath } = Util.getCacheFileInfo('coverage', dataId, options.cacheDir); await Util.writeFile(cachePath, JSON.stringify(results)); return emptyCoverageList; }; const getEmptyCoverages = async (testedMap, options, coverageType, fileList, fileTransformer) => { const sourceFilter = Util.getSourceFilter(options); const sourcePathReplacer = getSourcePathReplacer(options); const baseDir = options.baseDir; // console.log(fileList); const entryList = []; for (const item of fileList) { const { fileType, filePath } = item; const type = resolveFileType(fileType, filePath); const url = pathToFileURL(filePath).toString(); const source = Util.readFileSync(filePath); const entry = { empty: true, type, url, source }; // normalize sourcePath here let sourcePath = normalizeSourcePath(url, baseDir); if (sourcePathReplacer) { const newSourcePath = sourcePathReplacer(sourcePath, entry); if (typeof newSourcePath === 'string' && newSourcePath) { sourcePath = newSourcePath; } } // console.log(sourcePath); if (testedMap.has(sourcePath)) { continue; } if (!sourceFilter(sourcePath)) { continue; } entry.sourcePath = sourcePath; await fileTransformer(entry, coverageType); // after transformer entry.id = Util.calculateSha1(entry.sourcePath + entry.source); entryList.push(entry); } // console.log('fileList', fileList); if (!entryList.length) { return; } // save empty coverage for merging raw reports return getUntestedCoverageData(entryList, options, coverageType); }; const getUntestedList = (testedMap, options, coverageType = 'v8') => { const allOptions = resolveAllOptions(options.all); // console.log('allOptions', all, allOptions); if (!allOptions) { return; } const { dirList, fileFilter, fileTransformer } = allOptions; const fileList = []; dirList.forEach((dir) => { Util.forEachFile(dir, [], (fileName, fileDir) => { const filePath = path.resolve(fileDir, fileName); // return file extname for file type const fileType = fileFilter(filePath); if (fileType) { fileList.push({ filePath, fileType, fileDir, fileName }); } }); }); if (!fileList.length) { return; } return getEmptyCoverages(testedMap, options, coverageType, fileList, fileTransformer); }; module.exports = { getUntestedList };