@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.
@@ -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: Profiler.TakePreciseCoverageReturnType[];
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 { fileURLToPath, pathToFileURL } from 'url';
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
- onAfterSuiteRun({ coverage }) {
211
- this.coverages.push(coverage);
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 transformResults = normalizeTransformResults(this.ctx.projects.map((project) => project.vitenode.fetchCache));
217
- const merged = mergeProcessCovs(this.coverages);
218
- const scriptCoverages = merged.result.filter((result) => this.testExclude.shouldInstrument(fileURLToPath(result.url)));
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 = Array.from(scriptCoverages.map((r) => r.url));
221
- const untestedFiles = await this.getUntestedFiles(coveredFiles, transformResults);
222
- scriptCoverages.push(...untestedFiles);
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 converted = await Promise.all(scriptCoverages.map(async ({ url, functions }) => {
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, transformResults) {
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.href));
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 == null ? void 0 : transformResult.map;
307
- const code = transformResult == null ? void 0 : transformResult.code;
308
- const sourcesContent = ((_a = map == null ? void 0 : map.sourcesContent) == null ? void 0 : _a[0]) || await promises.readFile(filePath, "utf-8").catch(() => {
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(fetchCaches) {
375
+ function normalizeTransformResults(fetchCache) {
349
376
  const normalized = /* @__PURE__ */ new Map();
350
- for (const fetchCache of fetchCaches) {
351
- for (const [key, value] of fetchCache.entries()) {
352
- const cleanEntry = cleanUrl(key);
353
- if (!normalized.has(cleanEntry))
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.2",
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": ">=0.32.0 <1"
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.4",
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.2",
62
- "vitest": "1.0.0-beta.2"
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",