monocart-coverage-reports
Version:
A code coverage tool to generate native V8 reports or Istanbul reports.
236 lines (195 loc) • 6.77 kB
JavaScript
const path = require('path');
const { fileURLToPath, pathToFileURL } = require('url');
const Util = require('./util.js');
const resolveSourceRootUrl = (sourceName, sourceRoot) => {
// prepend sourceRoot
if (sourceRoot && !sourceName.startsWith(sourceRoot)) {
if (!sourceRoot.endsWith('/')) {
sourceRoot += '/';
}
return sourceRoot + sourceName;
}
return sourceName;
};
const resolveAnonymousUrl = (url, index, type) => {
if (url) {
return url;
}
// anonymous scripts will have __playwright_evaluation_script__ as their URL.
const list = ['anonymous'];
if (index) {
list.push(`-${index}`);
}
if (type) {
list.push(`.${type}`);
}
return list.join('');
};
const filterPath = (str) => {
// double \\ to /
// src/images/icons/ sync \.svg$
// src/images/icons/sync-.svg$
str = str.replace(/\\\\/g, '/');
str = str.replace(/\?/g, '/');
// decodeURI("%20") to space
str = decodeURI(str);
// remove / of start, end or double, ./ ../
const ls = str.split('/').map((it) => {
it = it.trim();
// remove illegal characters except /
it = it.replace(/[\\:*?"<>|]/g, '');
// space
it = it.replace(/\s+/g, '-');
return it;
}).filter((item) => {
if (!item || item === '.' || item === '..') {
return false;
}
return true;
});
return ls.join('/');
};
const normalizeSourcePath = (sourceUrl, baseDir) => {
// includes anonymous and everything
// file url
if (sourceUrl.startsWith('file:')) {
const relPath = Util.relativePath(fileURLToPath(sourceUrl), baseDir);
return filterPath(relPath);
}
// absolute path
// c:\\workspace\test\\selenium.spec.js true
// c://a.js true
// c:\\a.js true
// C:/a.js true
// /a.js true
// a.js false
// ws://a.js false
// file://a.js false
// http://a.com/a.js false
// webpack://src/a.js false
if (path.isAbsolute(sourceUrl)) {
const relPath = Util.relativePath(sourceUrl, baseDir);
return filterPath(relPath);
}
// ws://a.js
// http://127.0.0:8080/a.js
// https://127.0.0:8080/a.js?v=1
// webpack://coverage-v8/./test/mock/src/branch/branch.js
// webpack://monocart-v8/../starfall-cli/node_modules/css-loader/dist/runtime/api.js
// url http/https/webpack
const urlObj = Util.resolveUrl(sourceUrl);
if (urlObj) {
const host = [urlObj.hostname, urlObj.port].filter((it) => it).join('-');
// always start with '/'
const pathname = urlObj.pathname;
let p = host + pathname;
// webpack://monocart-v8/packages/v8/src/app.vue?5cc4
if (urlObj.search) {
p += `/${urlObj.search}`;
}
return filterPath(p);
}
// relative path
const fileUrl = pathToFileURL(sourceUrl).toString();
const relPath = Util.relativePath(fileURLToPath(fileUrl), baseDir);
return filterPath(relPath);
};
const getSourceType = (sourcePath) => {
let ext = path.extname(sourcePath);
let type = 'js';
if (ext) {
ext = ext.slice(1);
const reg = /^[a-z0-9]+$/;
if (reg.test(ext)) {
type = ext;
}
}
return type;
};
// ========================================================================================================
const getSourcePathReplacer = (options) => {
const input = options.sourcePath;
if (typeof input === 'function') {
return input;
}
// object replace key with value
if (input && typeof input === 'object') {
const keys = Object.keys(input).filter((k) => typeof input[k] === 'string');
if (keys.length) {
return (filePath) => {
keys.forEach((k) => {
filePath = filePath.replace(k, input[k]);
});
return filePath;
};
}
}
};
// ========================================================================================================
const initIstanbulSourcePath = (coverageData, fileSources, options) => {
const sourcePathReplacer = getSourcePathReplacer(options);
if (!sourcePathReplacer) {
return coverageData;
}
// console.log(fileSources);
const newCoverage = {};
Object.keys(coverageData).forEach((sourcePath) => {
// previous coverage and source
const item = coverageData[sourcePath];
let source = fileSources[sourcePath];
// source could be empty string ''
if (typeof source !== 'string') {
// read source with original source path first
source = Util.readFileSync(sourcePath);
if (typeof source === 'string') {
fileSources[sourcePath] = source;
}
}
// new source path, second is source url
const newSourcePath = sourcePathReplacer(sourcePath, item);
if (typeof newSourcePath === 'string' && newSourcePath) {
sourcePath = newSourcePath;
}
// update source path
item.data.path = sourcePath;
newCoverage[sourcePath] = item;
// update source for the new path
if (typeof source === 'string') {
fileSources[sourcePath] = source;
}
});
// console.log(Object.keys(fileSources));
// console.log(newCoverage);
return newCoverage;
};
// ========================================================================================================
const initSourceMapSourcesPath = (fileUrls, sourceMap, distFile, options) => {
const baseDir = options.baseDir;
const sourcePathReplacer = getSourcePathReplacer(options);
sourceMap.sources = sourceMap.sources.map((sourceName, i) => {
let sourceUrl = resolveSourceRootUrl(sourceName, sourceMap.sourceRoot);
sourceUrl = resolveAnonymousUrl(sourceUrl, i + 1, 'js');
let sourcePath = normalizeSourcePath(sourceUrl, baseDir);
// console.log(sourceUrl, sourcePath);
if (sourcePathReplacer) {
// sourcePath could be a filename only, may using distFile to get full path
const newSourcePath = sourcePathReplacer(sourcePath, {
url: sourceUrl,
distFile
});
if (typeof newSourcePath === 'string' && newSourcePath) {
sourcePath = newSourcePath;
}
}
fileUrls[sourcePath] = sourceUrl;
return sourcePath;
});
};
module.exports = {
resolveAnonymousUrl,
normalizeSourcePath,
getSourceType,
getSourcePathReplacer,
initIstanbulSourcePath,
initSourceMapSourcesPath
};