Socket
Socket
Sign inDemoInstall

@vitest/coverage-v8

Package Overview
Dependencies
Maintainers
3
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vitest/coverage-v8 - npm Package Compare versions

Comparing version 0.32.4 to 1.0.0-beta.6

10

dist/provider.d.ts

@@ -13,2 +13,3 @@ import { Profiler } from 'node:inspector';

excludeNodeModules?: boolean;
relativePath?: boolean;
}): {

@@ -20,2 +21,6 @@ shouldInstrument(filePath: string): boolean;

type Options = ResolvedCoverageOptions<'v8'>;
type RawCoverage = Profiler.TakePreciseCoverageReturnType;
type CoverageByTransformMode = Record<AfterSuiteRunMeta['transformMode'], RawCoverage[]>;
type ProjectName = NonNullable<AfterSuiteRunMeta['projectName']> | typeof DEFAULT_PROJECT;
declare const DEFAULT_PROJECT: unique symbol;
declare class V8CoverageProvider extends BaseCoverageProvider implements CoverageProvider {

@@ -26,12 +31,13 @@ name: string;

testExclude: InstanceType<TestExclude>;
coverages: Profiler.TakePreciseCoverageReturnType[];
coverages: Map<ProjectName, CoverageByTransformMode>;
initialize(ctx: Vitest): void;
resolveOptions(): Options;
clean(clean?: boolean): Promise<void>;
onAfterSuiteRun({ coverage }: AfterSuiteRunMeta): void;
onAfterSuiteRun({ coverage, transformMode, projectName }: AfterSuiteRunMeta): void;
reportCoverage({ allTestsRun }?: ReportContext): Promise<void>;
private getUntestedFiles;
private getSources;
private mergeAndTransformCoverage;
}
export { V8CoverageProvider };

200

dist/provider.js

@@ -1,3 +0,3 @@

import { existsSync, promises } from 'fs';
import { fileURLToPath, pathToFileURL } from 'url';
import { existsSync, promises, writeFileSync } from 'node:fs';
import { pathToFileURL, fileURLToPath } from 'node:url';
import v8ToIstanbul from 'v8-to-istanbul';

@@ -10,5 +10,7 @@ import { mergeProcessCovs } from '@bcoe/v8-coverage';

import MagicString from 'magic-string';
import { parseModule } from 'magicast';
import remapping from '@ampproject/remapping';
import c from 'picocolors';
import { provider } from 'std-env';
import { builtinModules } from 'node:module';
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config';

@@ -144,4 +146,30 @@ import { BaseCoverageProvider } from 'vitest/coverage';

const isWindows = process.platform === "win32";
const drive = isWindows ? process.cwd()[0] : null;
drive ? drive === drive.toUpperCase() ? drive.toLowerCase() : drive.toUpperCase() : null;
const queryRE = /\?.*$/s;
const hashRE = /#.*$/s;
function cleanUrl(url) {
return url.replace(hashRE, "").replace(queryRE, "");
}
/* @__PURE__ */ new Set([
...builtinModules,
"assert/strict",
"diagnostics_channel",
"dns/promises",
"fs/promises",
"path/posix",
"path/win32",
"readline/promises",
"stream/consumers",
"stream/promises",
"stream/web",
"timers/promises",
"util/types",
"wasi"
]);
const WRAPPER_LENGTH = 185;
const VITE_EXPORTS_LINE_PATTERN = /Object\.defineProperty\(__vite_ssr_exports__.*\n/g;
const DEFAULT_PROJECT = Symbol.for("default-project");
class V8CoverageProvider extends BaseCoverageProvider {

@@ -152,3 +180,3 @@ name = "v8";

testExclude;
coverages = [];
coverages = /* @__PURE__ */ new Map();
initialize(ctx) {

@@ -165,6 +193,9 @@ const config = ctx.config.coverage;

reportsDirectory: resolve(ctx.config.root, config.reportsDirectory || coverageConfigDefaults.reportsDirectory),
lines: config["100"] ? 100 : config.lines,
functions: config["100"] ? 100 : config.functions,
branches: config["100"] ? 100 : config.branches,
statements: config["100"] ? 100 : config.statements
thresholds: config.thresholds && {
...config.thresholds,
lines: config.thresholds["100"] ? 100 : config.thresholds.lines,
branches: config.thresholds["100"] ? 100 : config.thresholds.branches,
functions: config.thresholds["100"] ? 100 : config.thresholds.functions,
statements: config.thresholds["100"] ? 100 : config.thresholds.statements
}
};

@@ -176,3 +207,4 @@ this.testExclude = new _TestExclude({

excludeNodeModules: true,
extension: this.options.extension
extension: this.options.extension,
relativePath: !this.options.allowExternal
});

@@ -186,6 +218,18 @@ }

await promises.rm(this.options.reportsDirectory, { recursive: true, force: true, maxRetries: 10 });
this.coverages = [];
this.coverages = /* @__PURE__ */ new Map();
}
onAfterSuiteRun({ coverage }) {
this.coverages.push(coverage);
/*
* Coverage and meta information passed from Vitest runners.
* Note that adding new entries here and requiring on those without
* backwards compatibility is a breaking change.
*/
onAfterSuiteRun({ coverage, transformMode, projectName }) {
if (transformMode !== "web" && transformMode !== "ssr")
throw new Error(`Invalid transform mode: ${transformMode}`);
let entry = this.coverages.get(projectName || DEFAULT_PROJECT);
if (!entry) {
entry = { web: [], ssr: [] };
this.coverages.set(projectName || DEFAULT_PROJECT, entry);
}
entry[transformMode].push(coverage);
}

@@ -195,30 +239,22 @@ async reportCoverage({ allTestsRun } = {}) {

this.ctx.logger.log(c.blue(" % ") + c.yellow("@vitest/coverage-v8 does not work on Stackblitz. Report will be empty."));
const merged = mergeProcessCovs(this.coverages);
const scriptCoverages = merged.result.filter((result) => this.testExclude.shouldInstrument(fileURLToPath(result.url)));
const coverageMaps = await Promise.all(
Array.from(this.coverages.entries()).map(([projectName, coverages]) => [
this.mergeAndTransformCoverage(coverages.ssr, projectName, "ssr"),
this.mergeAndTransformCoverage(coverages.web, projectName, "web")
]).flat()
);
if (this.options.all && allTestsRun) {
const coveredFiles = Array.from(scriptCoverages.map((r) => r.url));
const untestedFiles = await this.getUntestedFiles(coveredFiles);
scriptCoverages.push(...untestedFiles);
const coveredFiles = coverageMaps.map((map) => map.files()).flat();
const untestedCoverage = await this.getUntestedFiles(coveredFiles);
const untestedCoverageResults = untestedCoverage.map((files) => ({ result: [files] }));
coverageMaps.push(await this.mergeAndTransformCoverage(untestedCoverageResults));
}
const converted = await Promise.all(scriptCoverages.map(async ({ url, functions }) => {
const sources = await this.getSources(url, functions);
const wrapperLength = sources.sourceMap ? WRAPPER_LENGTH : 0;
const converter = v8ToIstanbul(url, wrapperLength, sources);
await converter.load();
converter.applyCoverage(functions);
return converter.toIstanbul();
}));
const mergedCoverage = converted.reduce((coverage, previousCoverageMap) => {
const map = libCoverage.createCoverageMap(coverage);
map.merge(previousCoverageMap);
return map;
}, libCoverage.createCoverageMap({}));
const sourceMapStore = libSourceMaps.createSourceMapStore();
const coverageMap = await sourceMapStore.transformCoverage(mergedCoverage);
const coverageMap = mergeCoverageMaps(...coverageMaps);
const context = libReport.createContext({
dir: this.options.reportsDirectory,
coverageMap,
sourceFinder: sourceMapStore.sourceFinder,
watermarks: this.options.watermarks
});
if (hasTerminalReporter(this.options.reporter))
this.ctx.logger.log(c.blue(" % ") + c.dim("Coverage report from ") + c.yellow(this.name));
for (const reporter of this.options.reporter) {

@@ -231,33 +267,34 @@ reports.create(reporter[0], {

}
if (this.options.branches || this.options.functions || this.options.lines || this.options.statements) {
this.checkThresholds({
if (this.options.thresholds) {
const resolvedThresholds = this.resolveThresholds({
coverageMap,
thresholds: {
branches: this.options.branches,
functions: this.options.functions,
lines: this.options.lines,
statements: this.options.statements
},
perFile: this.options.perFile
thresholds: this.options.thresholds,
createCoverageMap: () => libCoverage.createCoverageMap({})
});
}
if (this.options.thresholdAutoUpdate && allTestsRun) {
this.updateThresholds({
coverageMap,
thresholds: {
branches: this.options.branches,
functions: this.options.functions,
lines: this.options.lines,
statements: this.options.statements
},
perFile: this.options.perFile,
configurationFile: this.ctx.server.config.configFile
this.checkThresholds({
thresholds: resolvedThresholds,
perFile: this.options.thresholds.perFile
});
if (this.options.thresholds.autoUpdate && allTestsRun) {
if (!this.ctx.server.config.configFile)
throw new Error('Missing configurationFile. The "coverage.thresholds.autoUpdate" can only be enabled when configuration file is used.');
const configFilePath = this.ctx.server.config.configFile;
const configModule = parseModule(await promises.readFile(configFilePath, "utf8"));
this.updateThresholds({
thresholds: resolvedThresholds,
perFile: this.options.thresholds.perFile,
configurationFile: {
write: () => writeFileSync(configFilePath, configModule.generate().code, "utf-8"),
read: () => configModule.exports.default.$type === "function-call" ? configModule.exports.default.$args[0] : configModule.exports.default
}
});
}
}
}
async getUntestedFiles(testedFiles) {
const transformResults = normalizeTransformResults(this.ctx.vitenode.fetchCache);
const includedFiles = await this.testExclude.glob(this.ctx.config.root);
const uncoveredFiles = includedFiles.map((file) => pathToFileURL(resolve(this.ctx.config.root, file))).filter((file) => !testedFiles.includes(file.href));
const uncoveredFiles = includedFiles.map((file) => pathToFileURL(resolve(this.ctx.config.root, file))).filter((file) => !testedFiles.includes(file.pathname));
return await Promise.all(uncoveredFiles.map(async (uncoveredFile) => {
const { source } = await this.getSources(uncoveredFile.href);
const { source } = await this.getSources(uncoveredFile.href, transformResults);
return {

@@ -280,12 +317,8 @@ url: uncoveredFile.href,

}
async getSources(url, functions = []) {
var _a;
async getSources(url, transformResults, functions = []) {
const filePath = normalize(fileURLToPath(url));
const transformResult = this.ctx.projects.map((project) => {
var _a2;
return (_a2 = project.vitenode.fetchCache.get(filePath)) == null ? void 0 : _a2.result;
}).filter(Boolean).shift();
const map = transformResult == null ? void 0 : transformResult.map;
const code = transformResult == null ? void 0 : transformResult.code;
const sourcesContent = ((_a = map == null ? void 0 : map.sourcesContent) == null ? void 0 : _a[0]) || await promises.readFile(filePath, "utf-8").catch(() => {
const transformResult = transformResults.get(filePath);
const map = transformResult?.map;
const code = transformResult?.code;
const sourcesContent = map?.sourcesContent?.[0] || await promises.readFile(filePath, "utf-8").catch(() => {
const length = findLongestFunctionLength(functions);

@@ -309,3 +342,28 @@ return ".".repeat(length);

}
async mergeAndTransformCoverage(coverages, projectName, transformMode) {
const viteNode = this.ctx.projects.find((project) => project.getName() === projectName)?.vitenode || this.ctx.vitenode;
const fetchCache = transformMode ? viteNode.fetchCaches[transformMode] : viteNode.fetchCache;
const transformResults = normalizeTransformResults(fetchCache);
const merged = mergeProcessCovs(coverages);
const scriptCoverages = merged.result.filter((result) => this.testExclude.shouldInstrument(fileURLToPath(result.url)));
const converted = await Promise.all(scriptCoverages.map(async ({ url, functions }) => {
const sources = await this.getSources(url, transformResults, functions);
const wrapperLength = sources.sourceMap ? WRAPPER_LENGTH : 0;
const converter = v8ToIstanbul(url, wrapperLength, sources);
await converter.load();
converter.applyCoverage(functions);
return converter.toIstanbul();
}));
const mergedCoverage = mergeCoverageMaps(...converted);
const sourceMapStore = libSourceMaps.createSourceMapStore();
return sourceMapStore.transformCoverage(mergedCoverage);
}
}
function mergeCoverageMaps(...coverageMaps) {
return coverageMaps.reduce((coverage, previousCoverageMap) => {
const map = libCoverage.createCoverageMap(coverage);
map.merge(previousCoverageMap);
return map;
}, libCoverage.createCoverageMap({}));
}
function removeViteHelpersFromSourceMaps(source, map) {

@@ -317,3 +375,3 @@ if (!source || !source.match(VITE_EXPORTS_LINE_PATTERN))

const mapWithoutHelpers = sourceWithoutHelpers.generateMap({
hires: true
hires: "boundary"
});

@@ -332,3 +390,15 @@ const combinedMap = remapping(

}
function normalizeTransformResults(fetchCache) {
const normalized = /* @__PURE__ */ new Map();
for (const [key, value] of fetchCache.entries()) {
const cleanEntry = cleanUrl(key);
if (!normalized.has(cleanEntry))
normalized.set(cleanEntry, value.result);
}
return normalized;
}
function hasTerminalReporter(reporters) {
return reporters.some(([reporter]) => reporter === "text" || reporter === "text-summary" || reporter === "text-lcov" || reporter === "teamcity");
}
export { V8CoverageProvider };
{
"name": "@vitest/coverage-v8",
"type": "module",
"version": "0.32.4",
"version": "1.0.0-beta.6",
"description": "V8 coverage provider for Vitest",

@@ -29,3 +29,3 @@ "author": "Anthony Fu <anthonyfu117@hotmail.com>",

"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"default": "./dist/index.js"
},

@@ -41,3 +41,3 @@ "./*": "./*"

"peerDependencies": {
"vitest": ">=0.32.0 <1"
"vitest": "^1.0.0-0"
},

@@ -47,20 +47,21 @@ "dependencies": {

"@bcoe/v8-coverage": "^0.2.3",
"istanbul-lib-coverage": "^3.2.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
"istanbul-lib-source-maps": "^4.0.1",
"istanbul-reports": "^3.1.5",
"magic-string": "^0.30.0",
"istanbul-reports": "^3.1.6",
"magic-string": "^0.30.5",
"magicast": "^0.3.2",
"picocolors": "^1.0.0",
"std-env": "^3.3.3",
"std-env": "^3.5.0",
"test-exclude": "^6.0.0",
"v8-to-istanbul": "^9.1.0"
"v8-to-istanbul": "^9.2.0"
},
"devDependencies": {
"@types/istanbul-lib-coverage": "^2.0.4",
"@types/istanbul-lib-report": "^3.0.0",
"@types/istanbul-lib-source-maps": "^4.0.1",
"@types/istanbul-reports": "^3.0.1",
"@types/istanbul-lib-coverage": "^2.0.6",
"@types/istanbul-lib-report": "^3.0.3",
"@types/istanbul-lib-source-maps": "^4.0.4",
"@types/istanbul-reports": "^3.0.4",
"pathe": "^1.1.1",
"vitest": "0.32.4",
"vite-node": "0.32.4"
"vitest": "1.0.0-beta.6",
"vite-node": "1.0.0-beta.6"
},

@@ -67,0 +68,0 @@ "scripts": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc