@vitest/coverage-v8 3.1.0-beta.1 → 3.1.0

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/browser.js CHANGED
@@ -1,59 +1,58 @@
1
1
  import { cdp } from '@vitest/browser/context';
2
- import { l as loadProvider } from './load-provider-Bl5rgjsL.js';
2
+ import { l as loadProvider } from './load-provider-pEnu7lco.js';
3
3
 
4
4
  const session = cdp();
5
5
  let enabled = false;
6
6
  const mod = {
7
- async startCoverage() {
8
- if (enabled) {
9
- return;
10
- }
11
- enabled = true;
12
- await session.send("Profiler.enable");
13
- await session.send("Profiler.startPreciseCoverage", {
14
- callCount: true,
15
- detailed: true
16
- });
17
- },
18
- async takeCoverage() {
19
- const coverage = await session.send("Profiler.takePreciseCoverage");
20
- const result = [];
21
- for (const entry of coverage.result) {
22
- if (filterResult(entry)) {
23
- result.push({
24
- ...entry,
25
- url: decodeURIComponent(entry.url.replace(window.location.origin, ""))
26
- });
27
- }
28
- }
29
- return { result };
30
- },
31
- stopCoverage() {
32
- },
33
- async getProvider() {
34
- return loadProvider();
35
- }
7
+ async startCoverage() {
8
+ if (enabled) {
9
+ return;
10
+ }
11
+ enabled = true;
12
+ await session.send("Profiler.enable");
13
+ await session.send("Profiler.startPreciseCoverage", {
14
+ callCount: true,
15
+ detailed: true
16
+ });
17
+ },
18
+ async takeCoverage() {
19
+ const coverage = await session.send("Profiler.takePreciseCoverage");
20
+ const result = [];
21
+ for (const entry of coverage.result) {
22
+ if (filterResult(entry)) {
23
+ result.push({
24
+ ...entry,
25
+ url: decodeURIComponent(entry.url.replace(window.location.origin, ""))
26
+ });
27
+ }
28
+ }
29
+ return { result };
30
+ },
31
+ stopCoverage() {},
32
+ async getProvider() {
33
+ return loadProvider();
34
+ }
36
35
  };
37
36
  function filterResult(coverage) {
38
- if (!coverage.url.startsWith(window.location.origin)) {
39
- return false;
40
- }
41
- if (coverage.url.includes("/node_modules/")) {
42
- return false;
43
- }
44
- if (coverage.url.includes("__vitest_browser__")) {
45
- return false;
46
- }
47
- if (coverage.url.includes("__vitest__/assets")) {
48
- return false;
49
- }
50
- if (coverage.url === window.location.href) {
51
- return false;
52
- }
53
- if (coverage.url.includes("?browserv=") || coverage.url.includes("&browserv=")) {
54
- return false;
55
- }
56
- return true;
37
+ if (!coverage.url.startsWith(window.location.origin)) {
38
+ return false;
39
+ }
40
+ if (coverage.url.includes("/node_modules/")) {
41
+ return false;
42
+ }
43
+ if (coverage.url.includes("__vitest_browser__")) {
44
+ return false;
45
+ }
46
+ if (coverage.url.includes("__vitest__/assets")) {
47
+ return false;
48
+ }
49
+ if (coverage.url === window.location.href) {
50
+ return false;
51
+ }
52
+ if (coverage.url.includes("?browserv=") || coverage.url.includes("&browserv=")) {
53
+ return false;
54
+ }
55
+ return true;
57
56
  }
58
57
 
59
58
  export { mod as default };
package/dist/index.js CHANGED
@@ -1,60 +1,60 @@
1
1
  import inspector from 'node:inspector';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { provider } from 'std-env';
4
- import { l as loadProvider } from './load-provider-Bl5rgjsL.js';
4
+ import { l as loadProvider } from './load-provider-pEnu7lco.js';
5
5
 
6
6
  const session = new inspector.Session();
7
7
  let enabled = false;
8
8
  const mod = {
9
- startCoverage({ isolate }) {
10
- if (isolate === false && enabled) {
11
- return;
12
- }
13
- enabled = true;
14
- session.connect();
15
- session.post("Profiler.enable");
16
- session.post("Profiler.startPreciseCoverage", {
17
- callCount: true,
18
- detailed: true
19
- });
20
- },
21
- takeCoverage(options) {
22
- return new Promise((resolve, reject) => {
23
- session.post("Profiler.takePreciseCoverage", async (error, coverage) => {
24
- if (error) {
25
- return reject(error);
26
- }
27
- const result = coverage.result.filter(filterResult).map((res) => ({
28
- ...res,
29
- startOffset: options?.moduleExecutionInfo?.get(fileURLToPath(res.url))?.startOffset || 0
30
- }));
31
- resolve({ result });
32
- });
33
- if (provider === "stackblitz") {
34
- resolve({ result: [] });
35
- }
36
- });
37
- },
38
- stopCoverage({ isolate }) {
39
- if (isolate === false) {
40
- return;
41
- }
42
- session.post("Profiler.stopPreciseCoverage");
43
- session.post("Profiler.disable");
44
- session.disconnect();
45
- },
46
- async getProvider() {
47
- return loadProvider();
48
- }
9
+ startCoverage({ isolate }) {
10
+ if (isolate === false && enabled) {
11
+ return;
12
+ }
13
+ enabled = true;
14
+ session.connect();
15
+ session.post("Profiler.enable");
16
+ session.post("Profiler.startPreciseCoverage", {
17
+ callCount: true,
18
+ detailed: true
19
+ });
20
+ },
21
+ takeCoverage(options) {
22
+ return new Promise((resolve, reject) => {
23
+ session.post("Profiler.takePreciseCoverage", async (error, coverage) => {
24
+ if (error) {
25
+ return reject(error);
26
+ }
27
+ const result = coverage.result.filter(filterResult).map((res) => ({
28
+ ...res,
29
+ startOffset: options?.moduleExecutionInfo?.get(fileURLToPath(res.url))?.startOffset || 0
30
+ }));
31
+ resolve({ result });
32
+ });
33
+ if (provider === "stackblitz") {
34
+ resolve({ result: [] });
35
+ }
36
+ });
37
+ },
38
+ stopCoverage({ isolate }) {
39
+ if (isolate === false) {
40
+ return;
41
+ }
42
+ session.post("Profiler.stopPreciseCoverage");
43
+ session.post("Profiler.disable");
44
+ session.disconnect();
45
+ },
46
+ async getProvider() {
47
+ return loadProvider();
48
+ }
49
49
  };
50
50
  function filterResult(coverage) {
51
- if (!coverage.url.startsWith("file://")) {
52
- return false;
53
- }
54
- if (coverage.url.includes("/node_modules/")) {
55
- return false;
56
- }
57
- return true;
51
+ if (!coverage.url.startsWith("file://")) {
52
+ return false;
53
+ }
54
+ if (coverage.url.includes("/node_modules/")) {
55
+ return false;
56
+ }
57
+ return true;
58
58
  }
59
59
 
60
60
  export { mod as default };
@@ -0,0 +1,10 @@
1
+ const name = "./provider.js";
2
+ async function loadProvider() {
3
+ const { V8CoverageProvider } = await import(
4
+ /* @vite-ignore */
5
+ name
6
+ );
7
+ return new V8CoverageProvider();
8
+ }
9
+
10
+ export { loadProvider as l };
@@ -22,4 +22,5 @@ declare class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOp
22
22
  private convertCoverage;
23
23
  }
24
24
 
25
- export { type ScriptCoverageWithOffset, V8CoverageProvider };
25
+ export { V8CoverageProvider };
26
+ export type { ScriptCoverageWithOffset };
package/dist/provider.js CHANGED
@@ -2520,323 +2520,283 @@ const drive = isWindows ? process.cwd()[0] : null;
2520
2520
  drive ? drive === drive.toUpperCase() ? drive.toLowerCase() : drive.toUpperCase() : null;
2521
2521
  const postfixRE = /[?#].*$/;
2522
2522
  function cleanUrl(url) {
2523
- return url.replace(postfixRE, "");
2523
+ return url.replace(postfixRE, "");
2524
2524
  }
2525
- /* @__PURE__ */ new Set([
2526
- ...builtinModules,
2527
- "assert/strict",
2528
- "diagnostics_channel",
2529
- "dns/promises",
2530
- "fs/promises",
2531
- "path/posix",
2532
- "path/win32",
2533
- "readline/promises",
2534
- "stream/consumers",
2535
- "stream/promises",
2536
- "stream/web",
2537
- "timers/promises",
2538
- "util/types",
2539
- "wasi"
2525
+ new Set([
2526
+ ...builtinModules,
2527
+ "assert/strict",
2528
+ "diagnostics_channel",
2529
+ "dns/promises",
2530
+ "fs/promises",
2531
+ "path/posix",
2532
+ "path/win32",
2533
+ "readline/promises",
2534
+ "stream/consumers",
2535
+ "stream/promises",
2536
+ "stream/web",
2537
+ "timers/promises",
2538
+ "util/types",
2539
+ "wasi"
2540
2540
  ]);
2541
2541
 
2542
- var version = "3.1.0-beta.1";
2542
+ var version = "3.1.0";
2543
2543
 
2544
2544
  const VITE_EXPORTS_LINE_PATTERN = /Object\.defineProperty\(__vite_ssr_exports__.*\n/g;
2545
2545
  const DECORATOR_METADATA_PATTERN = /_ts_metadata\("design:paramtypes", \[[^\]]*\]\),*/g;
2546
2546
  const FILE_PROTOCOL = "file://";
2547
2547
  const debug = createDebug("vitest:coverage");
2548
2548
  class V8CoverageProvider extends BaseCoverageProvider {
2549
- name = "v8";
2550
- version = version;
2551
- testExclude;
2552
- initialize(ctx) {
2553
- this._initialize(ctx);
2554
- this.testExclude = new TestExclude({
2555
- cwd: ctx.config.root,
2556
- include: this.options.include,
2557
- exclude: this.options.exclude,
2558
- excludeNodeModules: true,
2559
- extension: this.options.extension,
2560
- relativePath: !this.options.allowExternal
2561
- });
2562
- }
2563
- createCoverageMap() {
2564
- return libCoverage.createCoverageMap({});
2565
- }
2566
- async generateCoverage({ allTestsRun }) {
2567
- const coverageMap = this.createCoverageMap();
2568
- let merged = { result: [] };
2569
- await this.readCoverageFiles({
2570
- onFileRead(coverage) {
2571
- merged = mergeProcessCovs([merged, coverage]);
2572
- merged.result.forEach((result) => {
2573
- if (!result.startOffset) {
2574
- const original = coverage.result.find((r) => r.url === result.url);
2575
- result.startOffset = original?.startOffset || 0;
2576
- }
2577
- });
2578
- },
2579
- onFinished: async (project, transformMode) => {
2580
- const converted = await this.convertCoverage(
2581
- merged,
2582
- project,
2583
- transformMode
2584
- );
2585
- const transformedCoverage = await transformCoverage(converted);
2586
- coverageMap.merge(transformedCoverage);
2587
- merged = { result: [] };
2588
- },
2589
- onDebug: debug
2590
- });
2591
- if (this.options.all && (allTestsRun || !this.options.cleanOnRerun)) {
2592
- const coveredFiles = coverageMap.files();
2593
- const untestedCoverage = await this.getUntestedFiles(coveredFiles);
2594
- coverageMap.merge(await transformCoverage(untestedCoverage));
2595
- }
2596
- if (this.options.excludeAfterRemap) {
2597
- coverageMap.filter((filename) => this.testExclude.shouldInstrument(filename));
2598
- }
2599
- return coverageMap;
2600
- }
2601
- async generateReports(coverageMap, allTestsRun) {
2602
- if (provider === "stackblitz") {
2603
- this.ctx.logger.log(
2604
- c.blue(" % ") + c.yellow(
2605
- "@vitest/coverage-v8 does not work on Stackblitz. Report will be empty."
2606
- )
2607
- );
2608
- }
2609
- const context = libReport.createContext({
2610
- dir: this.options.reportsDirectory,
2611
- coverageMap,
2612
- watermarks: this.options.watermarks
2613
- });
2614
- if (this.hasTerminalReporter(this.options.reporter)) {
2615
- this.ctx.logger.log(
2616
- c.blue(" % ") + c.dim("Coverage report from ") + c.yellow(this.name)
2617
- );
2618
- }
2619
- for (const reporter of this.options.reporter) {
2620
- reports.create(reporter[0], {
2621
- skipFull: this.options.skipFull,
2622
- projectRoot: this.ctx.config.root,
2623
- ...reporter[1]
2624
- }).execute(context);
2625
- }
2626
- if (this.options.thresholds) {
2627
- await this.reportThresholds(coverageMap, allTestsRun);
2628
- }
2629
- }
2630
- async parseConfigModule(configFilePath) {
2631
- return parseModule(
2632
- await promises.readFile(configFilePath, "utf8")
2633
- );
2634
- }
2635
- async getUntestedFiles(testedFiles) {
2636
- const transformResults = normalizeTransformResults(
2637
- this.ctx.vitenode.fetchCache
2638
- );
2639
- const transform = this.createUncoveredFileTransformer(this.ctx);
2640
- const allFiles = await this.testExclude.glob(this.ctx.config.root);
2641
- let includedFiles = allFiles.map(
2642
- (file) => resolve(this.ctx.config.root, file)
2643
- );
2644
- if (this.ctx.config.changed) {
2645
- includedFiles = (this.ctx.config.related || []).filter(
2646
- (file) => includedFiles.includes(file)
2647
- );
2648
- }
2649
- const uncoveredFiles = includedFiles.map((file) => pathToFileURL(file)).filter((file) => !testedFiles.includes(file.pathname));
2650
- let index = 0;
2651
- const coverageMap = this.createCoverageMap();
2652
- for (const chunk of this.toSlices(uncoveredFiles, this.options.processingConcurrency)) {
2653
- if (debug.enabled) {
2654
- index += chunk.length;
2655
- debug("Uncovered files %d/%d", index, uncoveredFiles.length);
2656
- }
2657
- await Promise.all(chunk.map(async (filename) => {
2658
- const sources = await this.getSources(
2659
- filename.href,
2660
- transformResults,
2661
- transform
2662
- );
2663
- const converter = v8ToIstanbul(
2664
- filename.href,
2665
- 0,
2666
- sources,
2667
- void 0,
2668
- this.options.ignoreEmptyLines
2669
- );
2670
- await converter.load();
2671
- try {
2672
- converter.applyCoverage([{
2673
- ranges: [
2674
- {
2675
- startOffset: 0,
2676
- endOffset: sources.originalSource.length,
2677
- count: 0
2678
- }
2679
- ],
2680
- isBlockCoverage: true,
2681
- // This is magical value that indicates an empty report: https://github.com/istanbuljs/v8-to-istanbul/blob/fca5e6a9e6ef38a9cdc3a178d5a6cf9ef82e6cab/lib/v8-to-istanbul.js#LL131C40-L131C40
2682
- functionName: "(empty-report)"
2683
- }]);
2684
- } catch (error) {
2685
- this.ctx.logger.error(`Failed to convert coverage for uncovered ${filename.href}.
2686
- `, error);
2687
- }
2688
- coverageMap.merge(converter.toIstanbul());
2689
- }));
2690
- }
2691
- return coverageMap;
2692
- }
2693
- async getSources(url, transformResults, onTransform, functions = []) {
2694
- const filePath = normalize(fileURLToPath(url));
2695
- let transformResult = transformResults.get(filePath);
2696
- if (!transformResult) {
2697
- transformResult = await onTransform(removeStartsWith(url, FILE_PROTOCOL)).catch(() => void 0);
2698
- }
2699
- const map = transformResult?.map;
2700
- const code = transformResult?.code;
2701
- const sourcesContent = map?.sourcesContent || [];
2702
- if (!sourcesContent[0]) {
2703
- sourcesContent[0] = await promises.readFile(filePath, "utf-8").catch(() => {
2704
- const length = findLongestFunctionLength(functions);
2705
- return ".".repeat(length);
2706
- });
2707
- }
2708
- if (!map) {
2709
- return {
2710
- source: code || sourcesContent[0],
2711
- originalSource: sourcesContent[0]
2712
- };
2713
- }
2714
- const sources = (map.sources || []).filter((source) => source != null).map((source) => new URL(source, url).href);
2715
- if (sources.length === 0) {
2716
- sources.push(url);
2717
- }
2718
- return {
2719
- originalSource: sourcesContent[0],
2720
- source: code || sourcesContent[0],
2721
- sourceMap: {
2722
- sourcemap: excludeGeneratedCode(code, {
2723
- ...map,
2724
- version: 3,
2725
- sources,
2726
- sourcesContent
2727
- })
2728
- }
2729
- };
2730
- }
2731
- async convertCoverage(coverage, project = this.ctx.getRootProject(), transformMode) {
2732
- let fetchCache = project.vitenode.fetchCache;
2733
- if (transformMode) {
2734
- fetchCache = transformMode === "browser" ? /* @__PURE__ */ new Map() : project.vitenode.fetchCaches[transformMode];
2735
- }
2736
- const transformResults = normalizeTransformResults(fetchCache);
2737
- async function onTransform(filepath) {
2738
- if (transformMode === "browser" && project.browser) {
2739
- const result = await project.browser.vite.transformRequest(removeStartsWith(filepath, project.config.root));
2740
- if (result) {
2741
- return { ...result, code: `${result.code}// <inline-source-map>` };
2742
- }
2743
- }
2744
- return project.vitenode.transformRequest(filepath);
2745
- }
2746
- const scriptCoverages = [];
2747
- for (const result of coverage.result) {
2748
- if (transformMode === "browser") {
2749
- if (result.url.startsWith("/@fs")) {
2750
- result.url = `${FILE_PROTOCOL}${removeStartsWith(result.url, "/@fs")}`;
2751
- } else {
2752
- result.url = `${FILE_PROTOCOL}${project.config.root}${result.url}`;
2753
- }
2754
- }
2755
- if (this.testExclude.shouldInstrument(fileURLToPath(result.url))) {
2756
- scriptCoverages.push(result);
2757
- }
2758
- }
2759
- const coverageMap = this.createCoverageMap();
2760
- let index = 0;
2761
- for (const chunk of this.toSlices(scriptCoverages, this.options.processingConcurrency)) {
2762
- if (debug.enabled) {
2763
- index += chunk.length;
2764
- debug("Converting %d/%d", index, scriptCoverages.length);
2765
- }
2766
- await Promise.all(
2767
- chunk.map(async ({ url, functions, startOffset }) => {
2768
- const sources = await this.getSources(
2769
- url,
2770
- transformResults,
2771
- onTransform,
2772
- functions
2773
- );
2774
- const converter = v8ToIstanbul(
2775
- url,
2776
- startOffset,
2777
- sources,
2778
- void 0,
2779
- this.options.ignoreEmptyLines
2780
- );
2781
- await converter.load();
2782
- try {
2783
- converter.applyCoverage(functions);
2784
- } catch (error) {
2785
- this.ctx.logger.error(`Failed to convert coverage for ${url}.
2786
- `, error);
2787
- }
2788
- coverageMap.merge(converter.toIstanbul());
2789
- })
2790
- );
2791
- }
2792
- return coverageMap;
2793
- }
2549
+ name = "v8";
2550
+ version = version;
2551
+ testExclude;
2552
+ initialize(ctx) {
2553
+ this._initialize(ctx);
2554
+ this.testExclude = new TestExclude({
2555
+ cwd: ctx.config.root,
2556
+ include: this.options.include,
2557
+ exclude: this.options.exclude,
2558
+ excludeNodeModules: true,
2559
+ extension: this.options.extension,
2560
+ relativePath: !this.options.allowExternal
2561
+ });
2562
+ }
2563
+ createCoverageMap() {
2564
+ return libCoverage.createCoverageMap({});
2565
+ }
2566
+ async generateCoverage({ allTestsRun }) {
2567
+ const coverageMap = this.createCoverageMap();
2568
+ let merged = { result: [] };
2569
+ await this.readCoverageFiles({
2570
+ onFileRead(coverage) {
2571
+ merged = mergeProcessCovs([merged, coverage]);
2572
+ merged.result.forEach((result) => {
2573
+ if (!result.startOffset) {
2574
+ const original = coverage.result.find((r) => r.url === result.url);
2575
+ result.startOffset = original?.startOffset || 0;
2576
+ }
2577
+ });
2578
+ },
2579
+ onFinished: async (project, transformMode) => {
2580
+ const converted = await this.convertCoverage(merged, project, transformMode);
2581
+ const transformedCoverage = await transformCoverage(converted);
2582
+ coverageMap.merge(transformedCoverage);
2583
+ merged = { result: [] };
2584
+ },
2585
+ onDebug: debug
2586
+ });
2587
+ if (this.options.all && (allTestsRun || !this.options.cleanOnRerun)) {
2588
+ const coveredFiles = coverageMap.files();
2589
+ const untestedCoverage = await this.getUntestedFiles(coveredFiles);
2590
+ coverageMap.merge(await transformCoverage(untestedCoverage));
2591
+ }
2592
+ if (this.options.excludeAfterRemap) {
2593
+ coverageMap.filter((filename) => this.testExclude.shouldInstrument(filename));
2594
+ }
2595
+ return coverageMap;
2596
+ }
2597
+ async generateReports(coverageMap, allTestsRun) {
2598
+ if (provider === "stackblitz") {
2599
+ this.ctx.logger.log(c.blue(" % ") + c.yellow("@vitest/coverage-v8 does not work on Stackblitz. Report will be empty."));
2600
+ }
2601
+ const context = libReport.createContext({
2602
+ dir: this.options.reportsDirectory,
2603
+ coverageMap,
2604
+ watermarks: this.options.watermarks
2605
+ });
2606
+ if (this.hasTerminalReporter(this.options.reporter)) {
2607
+ this.ctx.logger.log(c.blue(" % ") + c.dim("Coverage report from ") + c.yellow(this.name));
2608
+ }
2609
+ for (const reporter of this.options.reporter) {
2610
+ reports.create(reporter[0], {
2611
+ skipFull: this.options.skipFull,
2612
+ projectRoot: this.ctx.config.root,
2613
+ ...reporter[1]
2614
+ }).execute(context);
2615
+ }
2616
+ if (this.options.thresholds) {
2617
+ await this.reportThresholds(coverageMap, allTestsRun);
2618
+ }
2619
+ }
2620
+ async parseConfigModule(configFilePath) {
2621
+ return parseModule(await promises.readFile(configFilePath, "utf8"));
2622
+ }
2623
+ async getUntestedFiles(testedFiles) {
2624
+ const transformResults = normalizeTransformResults(this.ctx.vitenode.fetchCache);
2625
+ const transform = this.createUncoveredFileTransformer(this.ctx);
2626
+ const allFiles = await this.testExclude.glob(this.ctx.config.root);
2627
+ let includedFiles = allFiles.map((file) => resolve(this.ctx.config.root, file));
2628
+ if (this.ctx.config.changed) {
2629
+ includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
2630
+ }
2631
+ const uncoveredFiles = includedFiles.map((file) => pathToFileURL(file)).filter((file) => !testedFiles.includes(file.pathname));
2632
+ let index = 0;
2633
+ const coverageMap = this.createCoverageMap();
2634
+ for (const chunk of this.toSlices(uncoveredFiles, this.options.processingConcurrency)) {
2635
+ if (debug.enabled) {
2636
+ index += chunk.length;
2637
+ debug("Uncovered files %d/%d", index, uncoveredFiles.length);
2638
+ }
2639
+ await Promise.all(chunk.map(async (filename) => {
2640
+ const sources = await this.getSources(filename.href, transformResults, transform);
2641
+ const converter = v8ToIstanbul(filename.href, 0, sources, undefined, this.options.ignoreEmptyLines);
2642
+ await converter.load();
2643
+ try {
2644
+ converter.applyCoverage([{
2645
+ ranges: [{
2646
+ startOffset: 0,
2647
+ endOffset: sources.originalSource.length,
2648
+ count: 0
2649
+ }],
2650
+ isBlockCoverage: true,
2651
+ functionName: "(empty-report)"
2652
+ }]);
2653
+ } catch (error) {
2654
+ this.ctx.logger.error(`Failed to convert coverage for uncovered ${filename.href}.\n`, error);
2655
+ }
2656
+ coverageMap.merge(converter.toIstanbul());
2657
+ }));
2658
+ }
2659
+ return coverageMap;
2660
+ }
2661
+ async getSources(url, transformResults, onTransform, functions = []) {
2662
+ const filePath = normalize(fileURLToPath(url));
2663
+ let transformResult = transformResults.get(filePath);
2664
+ if (!transformResult) {
2665
+ transformResult = await onTransform(removeStartsWith(url, FILE_PROTOCOL)).catch(() => undefined);
2666
+ }
2667
+ const map = transformResult?.map;
2668
+ const code = transformResult?.code;
2669
+ const sourcesContent = map?.sourcesContent || [];
2670
+ if (!sourcesContent[0]) {
2671
+ sourcesContent[0] = await promises.readFile(filePath, "utf-8").catch(() => {
2672
+ const length = findLongestFunctionLength(functions);
2673
+ return ".".repeat(length);
2674
+ });
2675
+ }
2676
+ if (!map) {
2677
+ return {
2678
+ source: code || sourcesContent[0],
2679
+ originalSource: sourcesContent[0]
2680
+ };
2681
+ }
2682
+ const sources = (map.sources || []).filter((source) => source != null).map((source) => new URL(source, url).href);
2683
+ if (sources.length === 0) {
2684
+ sources.push(url);
2685
+ }
2686
+ return {
2687
+ originalSource: sourcesContent[0],
2688
+ source: code || sourcesContent[0],
2689
+ sourceMap: { sourcemap: excludeGeneratedCode(code, {
2690
+ ...map,
2691
+ version: 3,
2692
+ sources,
2693
+ sourcesContent
2694
+ }) }
2695
+ };
2696
+ }
2697
+ async convertCoverage(coverage, project = this.ctx.getRootProject(), transformMode) {
2698
+ let fetchCache = project.vitenode.fetchCache;
2699
+ if (transformMode) {
2700
+ fetchCache = transformMode === "browser" ? new Map() : project.vitenode.fetchCaches[transformMode];
2701
+ }
2702
+ const transformResults = normalizeTransformResults(fetchCache);
2703
+ async function onTransform(filepath) {
2704
+ if (transformMode === "browser" && project.browser) {
2705
+ const result = await project.browser.vite.transformRequest(removeStartsWith(filepath, project.config.root));
2706
+ if (result) {
2707
+ return {
2708
+ ...result,
2709
+ code: `${result.code}// <inline-source-map>`
2710
+ };
2711
+ }
2712
+ }
2713
+ return project.vitenode.transformRequest(filepath);
2714
+ }
2715
+ const scriptCoverages = [];
2716
+ for (const result of coverage.result) {
2717
+ if (transformMode === "browser") {
2718
+ if (result.url.startsWith("/@fs")) {
2719
+ result.url = `${FILE_PROTOCOL}${removeStartsWith(result.url, "/@fs")}`;
2720
+ } else {
2721
+ result.url = `${FILE_PROTOCOL}${project.config.root}${result.url}`;
2722
+ }
2723
+ }
2724
+ if (this.testExclude.shouldInstrument(fileURLToPath(result.url))) {
2725
+ scriptCoverages.push(result);
2726
+ }
2727
+ }
2728
+ const coverageMap = this.createCoverageMap();
2729
+ let index = 0;
2730
+ for (const chunk of this.toSlices(scriptCoverages, this.options.processingConcurrency)) {
2731
+ if (debug.enabled) {
2732
+ index += chunk.length;
2733
+ debug("Converting %d/%d", index, scriptCoverages.length);
2734
+ }
2735
+ await Promise.all(chunk.map(async ({ url, functions, startOffset }) => {
2736
+ const sources = await this.getSources(url, transformResults, onTransform, functions);
2737
+ const converter = v8ToIstanbul(url, startOffset, sources, undefined, this.options.ignoreEmptyLines);
2738
+ await converter.load();
2739
+ try {
2740
+ converter.applyCoverage(functions);
2741
+ } catch (error) {
2742
+ this.ctx.logger.error(`Failed to convert coverage for ${url}.\n`, error);
2743
+ }
2744
+ coverageMap.merge(converter.toIstanbul());
2745
+ }));
2746
+ }
2747
+ return coverageMap;
2748
+ }
2794
2749
  }
2795
2750
  async function transformCoverage(coverageMap) {
2796
- const sourceMapStore = libSourceMaps.createSourceMapStore();
2797
- return await sourceMapStore.transformCoverage(coverageMap);
2751
+ const sourceMapStore = libSourceMaps.createSourceMapStore();
2752
+ return await sourceMapStore.transformCoverage(coverageMap);
2798
2753
  }
2754
+ /**
2755
+ * Remove generated code from the source maps:
2756
+ * - Vite's export helpers: e.g. `Object.defineProperty(__vite_ssr_exports__, "sum", { enumerable: true, configurable: true, get(){ return sum }});`
2757
+ * - SWC's decorator metadata: e.g. `_ts_metadata("design:paramtypes", [\ntypeof Request === "undefined" ? Object : Request\n]),`
2758
+ */
2799
2759
  function excludeGeneratedCode(source, map) {
2800
- if (!source) {
2801
- return map;
2802
- }
2803
- if (!source.match(VITE_EXPORTS_LINE_PATTERN) && !source.match(DECORATOR_METADATA_PATTERN)) {
2804
- return map;
2805
- }
2806
- const trimmed = new MagicString(source);
2807
- trimmed.replaceAll(VITE_EXPORTS_LINE_PATTERN, "\n");
2808
- trimmed.replaceAll(DECORATOR_METADATA_PATTERN, (match) => "\n".repeat(match.split("\n").length - 1));
2809
- const trimmedMap = trimmed.generateMap({ hires: "boundary" });
2810
- const combinedMap = remapping(
2811
- [{ ...trimmedMap, version: 3 }, map],
2812
- () => null
2813
- );
2814
- return combinedMap;
2760
+ if (!source) {
2761
+ return map;
2762
+ }
2763
+ if (!source.match(VITE_EXPORTS_LINE_PATTERN) && !source.match(DECORATOR_METADATA_PATTERN)) {
2764
+ return map;
2765
+ }
2766
+ const trimmed = new MagicString(source);
2767
+ trimmed.replaceAll(VITE_EXPORTS_LINE_PATTERN, "\n");
2768
+ trimmed.replaceAll(DECORATOR_METADATA_PATTERN, (match) => "\n".repeat(match.split("\n").length - 1));
2769
+ const trimmedMap = trimmed.generateMap({ hires: "boundary" });
2770
+ const combinedMap = remapping([{
2771
+ ...trimmedMap,
2772
+ version: 3
2773
+ }, map], () => null);
2774
+ return combinedMap;
2815
2775
  }
2776
+ /**
2777
+ * Find the function with highest `endOffset` to determine the length of the file
2778
+ */
2816
2779
  function findLongestFunctionLength(functions) {
2817
- return functions.reduce((previous, current) => {
2818
- const maxEndOffset = current.ranges.reduce(
2819
- (endOffset, range) => Math.max(endOffset, range.endOffset),
2820
- 0
2821
- );
2822
- return Math.max(previous, maxEndOffset);
2823
- }, 0);
2780
+ return functions.reduce((previous, current) => {
2781
+ const maxEndOffset = current.ranges.reduce((endOffset, range) => Math.max(endOffset, range.endOffset), 0);
2782
+ return Math.max(previous, maxEndOffset);
2783
+ }, 0);
2824
2784
  }
2825
2785
  function normalizeTransformResults(fetchCache) {
2826
- const normalized = /* @__PURE__ */ new Map();
2827
- for (const [key, value] of fetchCache.entries()) {
2828
- const cleanEntry = cleanUrl(key);
2829
- if (!normalized.has(cleanEntry)) {
2830
- normalized.set(cleanEntry, value.result);
2831
- }
2832
- }
2833
- return normalized;
2786
+ const normalized = new Map();
2787
+ for (const [key, value] of fetchCache.entries()) {
2788
+ const cleanEntry = cleanUrl(key);
2789
+ if (!normalized.has(cleanEntry)) {
2790
+ normalized.set(cleanEntry, value.result);
2791
+ }
2792
+ }
2793
+ return normalized;
2834
2794
  }
2835
2795
  function removeStartsWith(filepath, start) {
2836
- if (filepath.startsWith(start)) {
2837
- return filepath.slice(start.length);
2838
- }
2839
- return filepath;
2796
+ if (filepath.startsWith(start)) {
2797
+ return filepath.slice(start.length);
2798
+ }
2799
+ return filepath;
2840
2800
  }
2841
2801
 
2842
2802
  export { V8CoverageProvider };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/coverage-v8",
3
3
  "type": "module",
4
- "version": "3.1.0-beta.1",
4
+ "version": "3.1.0",
5
5
  "description": "V8 coverage provider for Vitest",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -41,8 +41,8 @@
41
41
  "dist"
42
42
  ],
43
43
  "peerDependencies": {
44
- "@vitest/browser": "3.1.0-beta.1",
45
- "vitest": "3.1.0-beta.1"
44
+ "@vitest/browser": "3.1.0",
45
+ "vitest": "3.1.0"
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "@vitest/browser": {
@@ -59,7 +59,7 @@
59
59
  "istanbul-reports": "^3.1.7",
60
60
  "magic-string": "^0.30.17",
61
61
  "magicast": "^0.3.5",
62
- "std-env": "^3.8.0",
62
+ "std-env": "^3.8.1",
63
63
  "test-exclude": "^7.0.1",
64
64
  "tinyrainbow": "^2.0.0"
65
65
  },
@@ -72,9 +72,9 @@
72
72
  "@types/test-exclude": "^6.0.2",
73
73
  "pathe": "^2.0.3",
74
74
  "v8-to-istanbul": "^9.3.0",
75
- "@vitest/browser": "3.1.0-beta.1",
76
- "vitest": "3.1.0-beta.1",
77
- "vite-node": "3.1.0-beta.1"
75
+ "vite-node": "3.1.0",
76
+ "@vitest/browser": "3.1.0",
77
+ "vitest": "3.1.0"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "rimraf dist && rollup -c",
@@ -1,10 +0,0 @@
1
- const name = "./provider.js";
2
- async function loadProvider() {
3
- const { V8CoverageProvider } = await import(
4
- /* @vite-ignore */
5
- name
6
- );
7
- return new V8CoverageProvider();
8
- }
9
-
10
- export { loadProvider as l };