@vitest/coverage-v8 1.0.0-beta.2 → 1.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/provider.d.ts +7 -2
- package/dist/provider.js +67 -42
- package/package.json +5 -5
package/dist/provider.d.ts
CHANGED
@@ -17,19 +17,24 @@ interface TestExclude {
|
|
17
17
|
};
|
18
18
|
}
|
19
19
|
type Options = ResolvedCoverageOptions<'v8'>;
|
20
|
+
type RawCoverage = Profiler.TakePreciseCoverageReturnType;
|
21
|
+
type CoverageByTransformMode = Record<AfterSuiteRunMeta['transformMode'], RawCoverage[]>;
|
22
|
+
type ProjectName = NonNullable<AfterSuiteRunMeta['projectName']> | typeof DEFAULT_PROJECT;
|
23
|
+
declare const DEFAULT_PROJECT: unique symbol;
|
20
24
|
declare class V8CoverageProvider extends BaseCoverageProvider implements CoverageProvider {
|
21
25
|
name: string;
|
22
26
|
ctx: Vitest;
|
23
27
|
options: Options;
|
24
28
|
testExclude: InstanceType<TestExclude>;
|
25
|
-
coverages:
|
29
|
+
coverages: Map<ProjectName, CoverageByTransformMode>;
|
26
30
|
initialize(ctx: Vitest): void;
|
27
31
|
resolveOptions(): Options;
|
28
32
|
clean(clean?: boolean): Promise<void>;
|
29
|
-
onAfterSuiteRun({ coverage }: AfterSuiteRunMeta): void;
|
33
|
+
onAfterSuiteRun({ coverage, transformMode, projectName }: AfterSuiteRunMeta): void;
|
30
34
|
reportCoverage({ allTestsRun }?: ReportContext): Promise<void>;
|
31
35
|
private getUntestedFiles;
|
32
36
|
private getSources;
|
37
|
+
private mergeAndTransformCoverage;
|
33
38
|
}
|
34
39
|
|
35
40
|
export { V8CoverageProvider };
|
package/dist/provider.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { existsSync, promises } from 'fs';
|
2
|
-
import {
|
1
|
+
import { existsSync, promises } from 'node:fs';
|
2
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
3
3
|
import v8ToIstanbul from 'v8-to-istanbul';
|
4
4
|
import { mergeProcessCovs } from '@bcoe/v8-coverage';
|
5
5
|
import libReport from 'istanbul-lib-report';
|
@@ -10,7 +10,7 @@ import MagicString from 'magic-string';
|
|
10
10
|
import remapping from '@ampproject/remapping';
|
11
11
|
import c from 'picocolors';
|
12
12
|
import { provider } from 'std-env';
|
13
|
-
import { builtinModules } from 'module';
|
13
|
+
import { builtinModules } from 'node:module';
|
14
14
|
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config';
|
15
15
|
import { BaseCoverageProvider } from 'vitest/coverage';
|
16
16
|
import _TestExclude from 'test-exclude';
|
@@ -168,12 +168,13 @@ function cleanUrl(url) {
|
|
168
168
|
|
169
169
|
const WRAPPER_LENGTH = 185;
|
170
170
|
const VITE_EXPORTS_LINE_PATTERN = /Object\.defineProperty\(__vite_ssr_exports__.*\n/g;
|
171
|
+
const DEFAULT_PROJECT = Symbol.for("default-project");
|
171
172
|
class V8CoverageProvider extends BaseCoverageProvider {
|
172
173
|
name = "v8";
|
173
174
|
ctx;
|
174
175
|
options;
|
175
176
|
testExclude;
|
176
|
-
coverages =
|
177
|
+
coverages = /* @__PURE__ */ new Map();
|
177
178
|
initialize(ctx) {
|
178
179
|
const config = ctx.config.coverage;
|
179
180
|
this.ctx = ctx;
|
@@ -205,41 +206,42 @@ class V8CoverageProvider extends BaseCoverageProvider {
|
|
205
206
|
async clean(clean = true) {
|
206
207
|
if (clean && existsSync(this.options.reportsDirectory))
|
207
208
|
await promises.rm(this.options.reportsDirectory, { recursive: true, force: true, maxRetries: 10 });
|
208
|
-
this.coverages =
|
209
|
+
this.coverages = /* @__PURE__ */ new Map();
|
209
210
|
}
|
210
|
-
|
211
|
-
|
211
|
+
/*
|
212
|
+
* Coverage and meta information passed from Vitest runners.
|
213
|
+
* Note that adding new entries here and requiring on those without
|
214
|
+
* backwards compatibility is a breaking change.
|
215
|
+
*/
|
216
|
+
onAfterSuiteRun({ coverage, transformMode, projectName }) {
|
217
|
+
if (transformMode !== "web" && transformMode !== "ssr")
|
218
|
+
throw new Error(`Invalid transform mode: ${transformMode}`);
|
219
|
+
let entry = this.coverages.get(projectName || DEFAULT_PROJECT);
|
220
|
+
if (!entry) {
|
221
|
+
entry = { web: [], ssr: [] };
|
222
|
+
this.coverages.set(projectName || DEFAULT_PROJECT, entry);
|
223
|
+
}
|
224
|
+
entry[transformMode].push(coverage);
|
212
225
|
}
|
213
226
|
async reportCoverage({ allTestsRun } = {}) {
|
214
227
|
if (provider === "stackblitz")
|
215
228
|
this.ctx.logger.log(c.blue(" % ") + c.yellow("@vitest/coverage-v8 does not work on Stackblitz. Report will be empty."));
|
216
|
-
const
|
217
|
-
|
218
|
-
|
229
|
+
const coverageMaps = await Promise.all(
|
230
|
+
Array.from(this.coverages.entries()).map(([projectName, coverages]) => [
|
231
|
+
this.mergeAndTransformCoverage(coverages.ssr, projectName, "ssr"),
|
232
|
+
this.mergeAndTransformCoverage(coverages.web, projectName, "web")
|
233
|
+
]).flat()
|
234
|
+
);
|
219
235
|
if (this.options.all && allTestsRun) {
|
220
|
-
const coveredFiles =
|
221
|
-
const
|
222
|
-
|
236
|
+
const coveredFiles = coverageMaps.map((map) => map.files()).flat();
|
237
|
+
const untestedCoverage = await this.getUntestedFiles(coveredFiles);
|
238
|
+
const untestedCoverageResults = untestedCoverage.map((files) => ({ result: [files] }));
|
239
|
+
coverageMaps.push(await this.mergeAndTransformCoverage(untestedCoverageResults));
|
223
240
|
}
|
224
|
-
const
|
225
|
-
const sources = await this.getSources(url, transformResults, functions);
|
226
|
-
const wrapperLength = sources.sourceMap ? WRAPPER_LENGTH : 0;
|
227
|
-
const converter = v8ToIstanbul(url, wrapperLength, sources);
|
228
|
-
await converter.load();
|
229
|
-
converter.applyCoverage(functions);
|
230
|
-
return converter.toIstanbul();
|
231
|
-
}));
|
232
|
-
const mergedCoverage = converted.reduce((coverage, previousCoverageMap) => {
|
233
|
-
const map = libCoverage.createCoverageMap(coverage);
|
234
|
-
map.merge(previousCoverageMap);
|
235
|
-
return map;
|
236
|
-
}, libCoverage.createCoverageMap({}));
|
237
|
-
const sourceMapStore = libSourceMaps.createSourceMapStore();
|
238
|
-
const coverageMap = await sourceMapStore.transformCoverage(mergedCoverage);
|
241
|
+
const coverageMap = mergeCoverageMaps(...coverageMaps);
|
239
242
|
const context = libReport.createContext({
|
240
243
|
dir: this.options.reportsDirectory,
|
241
244
|
coverageMap,
|
242
|
-
sourceFinder: sourceMapStore.sourceFinder,
|
243
245
|
watermarks: this.options.watermarks
|
244
246
|
});
|
245
247
|
if (hasTerminalReporter(this.options.reporter))
|
@@ -277,9 +279,10 @@ class V8CoverageProvider extends BaseCoverageProvider {
|
|
277
279
|
});
|
278
280
|
}
|
279
281
|
}
|
280
|
-
async getUntestedFiles(testedFiles
|
282
|
+
async getUntestedFiles(testedFiles) {
|
283
|
+
const transformResults = normalizeTransformResults(this.ctx.vitenode.fetchCache);
|
281
284
|
const includedFiles = await this.testExclude.glob(this.ctx.config.root);
|
282
|
-
const uncoveredFiles = includedFiles.map((file) => pathToFileURL(resolve(this.ctx.config.root, file))).filter((file) => !testedFiles.includes(file.
|
285
|
+
const uncoveredFiles = includedFiles.map((file) => pathToFileURL(resolve(this.ctx.config.root, file))).filter((file) => !testedFiles.includes(file.pathname));
|
283
286
|
return await Promise.all(uncoveredFiles.map(async (uncoveredFile) => {
|
284
287
|
const { source } = await this.getSources(uncoveredFile.href, transformResults);
|
285
288
|
return {
|
@@ -300,12 +303,11 @@ class V8CoverageProvider extends BaseCoverageProvider {
|
|
300
303
|
}));
|
301
304
|
}
|
302
305
|
async getSources(url, transformResults, functions = []) {
|
303
|
-
var _a;
|
304
306
|
const filePath = normalize(fileURLToPath(url));
|
305
307
|
const transformResult = transformResults.get(filePath);
|
306
|
-
const map = transformResult
|
307
|
-
const code = transformResult
|
308
|
-
const sourcesContent =
|
308
|
+
const map = transformResult?.map;
|
309
|
+
const code = transformResult?.code;
|
310
|
+
const sourcesContent = map?.sourcesContent?.[0] || await promises.readFile(filePath, "utf-8").catch(() => {
|
309
311
|
const length = findLongestFunctionLength(functions);
|
310
312
|
return ".".repeat(length);
|
311
313
|
});
|
@@ -324,6 +326,31 @@ class V8CoverageProvider extends BaseCoverageProvider {
|
|
324
326
|
}
|
325
327
|
};
|
326
328
|
}
|
329
|
+
async mergeAndTransformCoverage(coverages, projectName, transformMode) {
|
330
|
+
const viteNode = this.ctx.projects.find((project) => project.getName() === projectName)?.vitenode || this.ctx.vitenode;
|
331
|
+
const fetchCache = transformMode ? viteNode.fetchCaches[transformMode] : viteNode.fetchCache;
|
332
|
+
const transformResults = normalizeTransformResults(fetchCache);
|
333
|
+
const merged = mergeProcessCovs(coverages);
|
334
|
+
const scriptCoverages = merged.result.filter((result) => this.testExclude.shouldInstrument(fileURLToPath(result.url)));
|
335
|
+
const converted = await Promise.all(scriptCoverages.map(async ({ url, functions }) => {
|
336
|
+
const sources = await this.getSources(url, transformResults, functions);
|
337
|
+
const wrapperLength = sources.sourceMap ? WRAPPER_LENGTH : 0;
|
338
|
+
const converter = v8ToIstanbul(url, wrapperLength, sources);
|
339
|
+
await converter.load();
|
340
|
+
converter.applyCoverage(functions);
|
341
|
+
return converter.toIstanbul();
|
342
|
+
}));
|
343
|
+
const mergedCoverage = mergeCoverageMaps(...converted);
|
344
|
+
const sourceMapStore = libSourceMaps.createSourceMapStore();
|
345
|
+
return sourceMapStore.transformCoverage(mergedCoverage);
|
346
|
+
}
|
347
|
+
}
|
348
|
+
function mergeCoverageMaps(...coverageMaps) {
|
349
|
+
return coverageMaps.reduce((coverage, previousCoverageMap) => {
|
350
|
+
const map = libCoverage.createCoverageMap(coverage);
|
351
|
+
map.merge(previousCoverageMap);
|
352
|
+
return map;
|
353
|
+
}, libCoverage.createCoverageMap({}));
|
327
354
|
}
|
328
355
|
function removeViteHelpersFromSourceMaps(source, map) {
|
329
356
|
if (!source || !source.match(VITE_EXPORTS_LINE_PATTERN))
|
@@ -345,14 +372,12 @@ function findLongestFunctionLength(functions) {
|
|
345
372
|
return Math.max(previous, maxEndOffset);
|
346
373
|
}, 0);
|
347
374
|
}
|
348
|
-
function normalizeTransformResults(
|
375
|
+
function normalizeTransformResults(fetchCache) {
|
349
376
|
const normalized = /* @__PURE__ */ new Map();
|
350
|
-
for (const
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
normalized.set(cleanEntry, value.result);
|
355
|
-
}
|
377
|
+
for (const [key, value] of fetchCache.entries()) {
|
378
|
+
const cleanEntry = cleanUrl(key);
|
379
|
+
if (!normalized.has(cleanEntry))
|
380
|
+
normalized.set(cleanEntry, value.result);
|
356
381
|
}
|
357
382
|
return normalized;
|
358
383
|
}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitest/coverage-v8",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.0.0-beta.
|
4
|
+
"version": "1.0.0-beta.4",
|
5
5
|
"description": "V8 coverage provider for Vitest",
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
7
7
|
"license": "MIT",
|
@@ -37,7 +37,7 @@
|
|
37
37
|
"dist"
|
38
38
|
],
|
39
39
|
"peerDependencies": {
|
40
|
-
"vitest": "
|
40
|
+
"vitest": "^1.0.0-0"
|
41
41
|
},
|
42
42
|
"dependencies": {
|
43
43
|
"@ampproject/remapping": "^2.2.1",
|
@@ -46,7 +46,7 @@
|
|
46
46
|
"istanbul-lib-report": "^3.0.1",
|
47
47
|
"istanbul-lib-source-maps": "^4.0.1",
|
48
48
|
"istanbul-reports": "^3.1.5",
|
49
|
-
"magic-string": "^0.30.
|
49
|
+
"magic-string": "^0.30.5",
|
50
50
|
"picocolors": "^1.0.0",
|
51
51
|
"std-env": "^3.3.3",
|
52
52
|
"test-exclude": "^6.0.0",
|
@@ -58,8 +58,8 @@
|
|
58
58
|
"@types/istanbul-lib-source-maps": "^4.0.1",
|
59
59
|
"@types/istanbul-reports": "^3.0.1",
|
60
60
|
"pathe": "^1.1.1",
|
61
|
-
"vite-node": "1.0.0-beta.
|
62
|
-
"vitest": "1.0.0-beta.
|
61
|
+
"vite-node": "1.0.0-beta.4",
|
62
|
+
"vitest": "1.0.0-beta.4"
|
63
63
|
},
|
64
64
|
"scripts": {
|
65
65
|
"build": "rimraf dist && rollup -c",
|