@vitest/coverage-v8 4.0.0-beta.1 → 4.0.0-beta.11

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021-Present Vitest Team
3
+ Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- import inspector from 'node:inspector';
1
+ import inspector from 'node:inspector/promises';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { provider } from 'std-env';
4
4
  import { l as loadProvider } from './load-provider-CdgAx3rL.js';
5
+ import { n as normalize } from './pathe.M-eThtNZ-BTaAGrLg.js';
5
6
 
6
7
  const session = new inspector.Session();
7
8
  let enabled = false;
@@ -12,39 +13,35 @@ const mod = {
12
13
  }
13
14
  enabled = true;
14
15
  session.connect();
15
- await new Promise((resolve) => session.post("Profiler.enable", resolve));
16
- await new Promise((resolve) => session.post("Profiler.startPreciseCoverage", {
16
+ await session.post("Profiler.enable");
17
+ await session.post("Profiler.startPreciseCoverage", {
17
18
  callCount: true,
18
19
  detailed: true
19
- }, resolve));
20
+ });
20
21
  },
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
- try {
28
- const result = coverage.result.filter(filterResult).map((res) => ({
29
- ...res,
30
- startOffset: options?.moduleExecutionInfo?.get(fileURLToPath(res.url))?.startOffset || 0
31
- }));
32
- resolve({ result });
33
- } catch (e) {
34
- reject(e);
35
- }
36
- });
37
- if (provider === "stackblitz") {
38
- resolve({ result: [] });
22
+ async takeCoverage(options) {
23
+ if (provider === "stackblitz") {
24
+ return { result: [] };
25
+ }
26
+ const coverage = await session.post("Profiler.takePreciseCoverage");
27
+ const result = [];
28
+ // Reduce amount of data sent over rpc by doing some early result filtering
29
+ for (const entry of coverage.result) {
30
+ if (filterResult(entry)) {
31
+ result.push({
32
+ ...entry,
33
+ startOffset: options?.moduleExecutionInfo?.get(normalize(fileURLToPath(entry.url)))?.startOffset || 0
34
+ });
39
35
  }
40
- });
36
+ }
37
+ return { result };
41
38
  },
42
39
  async stopCoverage({ isolate }) {
43
40
  if (isolate === false) {
44
41
  return;
45
42
  }
46
- await new Promise((resolve) => session.post("Profiler.stopPreciseCoverage", resolve));
47
- await new Promise((resolve) => session.post("Profiler.disable", resolve));
43
+ await session.post("Profiler.stopPreciseCoverage");
44
+ await session.post("Profiler.disable");
48
45
  session.disconnect();
49
46
  },
50
47
  async getProvider() {
@@ -0,0 +1,104 @@
1
+ const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
2
+ function normalizeWindowsPath(input = "") {
3
+ if (!input) {
4
+ return input;
5
+ }
6
+ return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
7
+ }
8
+
9
+ const _UNC_REGEX = /^[/\\]{2}/;
10
+ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
11
+ const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
12
+ const normalize = function(path) {
13
+ if (path.length === 0) {
14
+ return ".";
15
+ }
16
+ path = normalizeWindowsPath(path);
17
+ const isUNCPath = path.match(_UNC_REGEX);
18
+ const isPathAbsolute = isAbsolute(path);
19
+ const trailingSeparator = path[path.length - 1] === "/";
20
+ path = normalizeString(path, !isPathAbsolute);
21
+ if (path.length === 0) {
22
+ if (isPathAbsolute) {
23
+ return "/";
24
+ }
25
+ return trailingSeparator ? "./" : ".";
26
+ }
27
+ if (trailingSeparator) {
28
+ path += "/";
29
+ }
30
+ if (_DRIVE_LETTER_RE.test(path)) {
31
+ path += "/";
32
+ }
33
+ if (isUNCPath) {
34
+ if (!isPathAbsolute) {
35
+ return `//./${path}`;
36
+ }
37
+ return `//${path}`;
38
+ }
39
+ return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
40
+ };
41
+ function normalizeString(path, allowAboveRoot) {
42
+ let res = "";
43
+ let lastSegmentLength = 0;
44
+ let lastSlash = -1;
45
+ let dots = 0;
46
+ let char = null;
47
+ for (let index = 0; index <= path.length; ++index) {
48
+ if (index < path.length) {
49
+ char = path[index];
50
+ } else if (char === "/") {
51
+ break;
52
+ } else {
53
+ char = "/";
54
+ }
55
+ if (char === "/") {
56
+ if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
57
+ if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
58
+ if (res.length > 2) {
59
+ const lastSlashIndex = res.lastIndexOf("/");
60
+ if (lastSlashIndex === -1) {
61
+ res = "";
62
+ lastSegmentLength = 0;
63
+ } else {
64
+ res = res.slice(0, lastSlashIndex);
65
+ lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
66
+ }
67
+ lastSlash = index;
68
+ dots = 0;
69
+ continue;
70
+ } else if (res.length > 0) {
71
+ res = "";
72
+ lastSegmentLength = 0;
73
+ lastSlash = index;
74
+ dots = 0;
75
+ continue;
76
+ }
77
+ }
78
+ if (allowAboveRoot) {
79
+ res += res.length > 0 ? "/.." : "..";
80
+ lastSegmentLength = 2;
81
+ }
82
+ } else {
83
+ if (res.length > 0) {
84
+ res += `/${path.slice(lastSlash + 1, index)}`;
85
+ } else {
86
+ res = path.slice(lastSlash + 1, index);
87
+ }
88
+ lastSegmentLength = index - lastSlash - 1;
89
+ }
90
+ lastSlash = index;
91
+ dots = 0;
92
+ } else if (char === "." && dots !== -1) {
93
+ ++dots;
94
+ } else {
95
+ dots = -1;
96
+ }
97
+ }
98
+ return res;
99
+ }
100
+ const isAbsolute = function(p) {
101
+ return _IS_ABSOLUTE_RE.test(p);
102
+ };
103
+
104
+ export { normalize as n };
package/dist/provider.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { promises } from 'node:fs';
2
- import { pathToFileURL, fileURLToPath } from 'node:url';
2
+ import { fileURLToPath } from 'node:url';
3
3
  import { mergeProcessCovs } from '@bcoe/v8-coverage';
4
4
  import astV8ToIstanbul from 'ast-v8-to-istanbul';
5
5
  import createDebug from 'debug';
@@ -10,138 +10,11 @@ import reports from 'istanbul-reports';
10
10
  import { parseModule } from 'magicast';
11
11
  import { provider } from 'std-env';
12
12
  import c from 'tinyrainbow';
13
- import { builtinModules } from 'node:module';
14
13
  import { BaseCoverageProvider } from 'vitest/coverage';
15
14
  import { parseAstAsync } from 'vitest/node';
15
+ import { n as normalize } from './pathe.M-eThtNZ-BTaAGrLg.js';
16
16
 
17
- const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
18
- function normalizeWindowsPath(input = "") {
19
- if (!input) {
20
- return input;
21
- }
22
- return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
23
- }
24
-
25
- const _UNC_REGEX = /^[/\\]{2}/;
26
- const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
27
- const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
28
- const normalize = function(path) {
29
- if (path.length === 0) {
30
- return ".";
31
- }
32
- path = normalizeWindowsPath(path);
33
- const isUNCPath = path.match(_UNC_REGEX);
34
- const isPathAbsolute = isAbsolute(path);
35
- const trailingSeparator = path[path.length - 1] === "/";
36
- path = normalizeString(path, !isPathAbsolute);
37
- if (path.length === 0) {
38
- if (isPathAbsolute) {
39
- return "/";
40
- }
41
- return trailingSeparator ? "./" : ".";
42
- }
43
- if (trailingSeparator) {
44
- path += "/";
45
- }
46
- if (_DRIVE_LETTER_RE.test(path)) {
47
- path += "/";
48
- }
49
- if (isUNCPath) {
50
- if (!isPathAbsolute) {
51
- return `//./${path}`;
52
- }
53
- return `//${path}`;
54
- }
55
- return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
56
- };
57
- function normalizeString(path, allowAboveRoot) {
58
- let res = "";
59
- let lastSegmentLength = 0;
60
- let lastSlash = -1;
61
- let dots = 0;
62
- let char = null;
63
- for (let index = 0; index <= path.length; ++index) {
64
- if (index < path.length) {
65
- char = path[index];
66
- } else if (char === "/") {
67
- break;
68
- } else {
69
- char = "/";
70
- }
71
- if (char === "/") {
72
- if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
73
- if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
74
- if (res.length > 2) {
75
- const lastSlashIndex = res.lastIndexOf("/");
76
- if (lastSlashIndex === -1) {
77
- res = "";
78
- lastSegmentLength = 0;
79
- } else {
80
- res = res.slice(0, lastSlashIndex);
81
- lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
82
- }
83
- lastSlash = index;
84
- dots = 0;
85
- continue;
86
- } else if (res.length > 0) {
87
- res = "";
88
- lastSegmentLength = 0;
89
- lastSlash = index;
90
- dots = 0;
91
- continue;
92
- }
93
- }
94
- if (allowAboveRoot) {
95
- res += res.length > 0 ? "/.." : "..";
96
- lastSegmentLength = 2;
97
- }
98
- } else {
99
- if (res.length > 0) {
100
- res += `/${path.slice(lastSlash + 1, index)}`;
101
- } else {
102
- res = path.slice(lastSlash + 1, index);
103
- }
104
- lastSegmentLength = index - lastSlash - 1;
105
- }
106
- lastSlash = index;
107
- dots = 0;
108
- } else if (char === "." && dots !== -1) {
109
- ++dots;
110
- } else {
111
- dots = -1;
112
- }
113
- }
114
- return res;
115
- }
116
- const isAbsolute = function(p) {
117
- return _IS_ABSOLUTE_RE.test(p);
118
- };
119
-
120
- const isWindows = process.platform === "win32";
121
- const drive = isWindows ? process.cwd()[0] : null;
122
- drive ? drive === drive.toUpperCase() ? drive.toLowerCase() : drive.toUpperCase() : null;
123
- const postfixRE = /[?#].*$/;
124
- function cleanUrl(url) {
125
- return url.replace(postfixRE, "");
126
- }
127
- new Set([
128
- ...builtinModules,
129
- "assert/strict",
130
- "diagnostics_channel",
131
- "dns/promises",
132
- "fs/promises",
133
- "path/posix",
134
- "path/win32",
135
- "readline/promises",
136
- "stream/consumers",
137
- "stream/promises",
138
- "stream/web",
139
- "timers/promises",
140
- "util/types",
141
- "wasi"
142
- ]);
143
-
144
- var version = "4.0.0-beta.1";
17
+ var version = "4.0.0-beta.11";
145
18
 
146
19
  const FILE_PROTOCOL = "file://";
147
20
  const debug = createDebug("vitest:coverage");
@@ -169,8 +42,8 @@ class V8CoverageProvider extends BaseCoverageProvider {
169
42
  }
170
43
  });
171
44
  },
172
- onFinished: async (project, transformMode) => {
173
- const converted = await this.convertCoverage(merged, project, transformMode);
45
+ onFinished: async (project, environment) => {
46
+ const converted = await this.convertCoverage(merged, project, environment);
174
47
  // Source maps can change based on projectName and transform mode.
175
48
  // Coverage transform re-uses source maps so we need to separate transforms from each other.
176
49
  const transformedCoverage = await transformCoverage(converted);
@@ -222,7 +95,6 @@ class V8CoverageProvider extends BaseCoverageProvider {
222
95
  return parseModule(await promises.readFile(configFilePath, "utf8"));
223
96
  }
224
97
  async getCoverageMapForUncoveredFiles(testedFiles) {
225
- const transformResults = normalizeTransformResults(this.ctx.vitenode.fetchCache);
226
98
  const transform = this.createUncoveredFileTransformer(this.ctx);
227
99
  const uncoveredFiles = await this.getUntestedFiles(testedFiles);
228
100
  let index = 0;
@@ -239,9 +111,10 @@ class V8CoverageProvider extends BaseCoverageProvider {
239
111
  start = performance.now();
240
112
  timeout = setTimeout(() => debug(c.bgRed(`File "${filename}" is taking longer than 3s`)), 3e3);
241
113
  }
242
- const url = pathToFileURL(filename);
243
- const sources = await this.getSources(url.href, transformResults, transform);
244
- coverageMap.merge(await this.remapCoverage(url.href, 0, sources, []));
114
+ // Do not use pathToFileURL to avoid encoding filename parts
115
+ const url = `file://${filename[0] === "/" ? "" : "/"}${filename}`;
116
+ const sources = await this.getSources(url, transform);
117
+ coverageMap.merge(await this.remapCoverage(url, 0, sources, []));
245
118
  if (debug.enabled) {
246
119
  clearTimeout(timeout);
247
120
  const diff = performance.now() - start;
@@ -298,6 +171,10 @@ class V8CoverageProvider extends BaseCoverageProvider {
298
171
  if (type === "statement" && node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" && node.expression.left.type === "MemberExpression" && node.expression.left.object.type === "MetaProperty" && node.expression.left.object.meta.name === "import" && node.expression.left.object.property.name === "meta" && node.expression.left.property.type === "Identifier" && node.expression.left.property.name === "env") {
299
172
  return true;
300
173
  }
174
+ // SSR mode's "import.meta.env ="
175
+ if (type === "statement" && node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" && node.expression.left.type === "MemberExpression" && node.expression.left.object.type === "Identifier" && node.expression.left.object.name === "__vite_ssr_import_meta__") {
176
+ return true;
177
+ }
301
178
  // SWC's decorators
302
179
  if (type === "statement" && node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.type === "Identifier" && node.expression.callee.name === "_ts_decorate") {
303
180
  return "ignore-this-and-nested-nodes";
@@ -305,15 +182,12 @@ class V8CoverageProvider extends BaseCoverageProvider {
305
182
  }
306
183
  });
307
184
  }
308
- async getSources(url, transformResults, onTransform, functions = []) {
309
- const filePath = normalize(fileURLToPath(url));
310
- let transformResult = transformResults.get(filePath);
311
- if (!transformResult) {
312
- transformResult = await onTransform(removeStartsWith(url, FILE_PROTOCOL)).catch(() => undefined);
313
- }
185
+ async getSources(url, onTransform, functions = []) {
186
+ const transformResult = await onTransform(removeStartsWith(url, FILE_PROTOCOL)).catch(() => undefined);
314
187
  const map = transformResult?.map;
315
188
  const code = transformResult?.code;
316
- if (!code) {
189
+ if (code == null) {
190
+ const filePath = normalize(fileURLToPath(url));
317
191
  const original = await promises.readFile(filePath, "utf-8").catch(() => {
318
192
  // If file does not exist construct a dummy source for it.
319
193
  // These can be files that were generated dynamically during the test run and were removed after it.
@@ -335,14 +209,12 @@ class V8CoverageProvider extends BaseCoverageProvider {
335
209
  map
336
210
  };
337
211
  }
338
- async convertCoverage(coverage, project = this.ctx.getRootProject(), transformMode) {
339
- let fetchCache = project.vitenode.fetchCache;
340
- if (transformMode) {
341
- fetchCache = transformMode === "browser" ? new Map() : project.vitenode.fetchCaches[transformMode];
212
+ async convertCoverage(coverage, project = this.ctx.getRootProject(), environment) {
213
+ if (environment === "__browser__" && !project.browser) {
214
+ throw new Error(`Cannot access browser module graph because it was torn down.`);
342
215
  }
343
- const transformResults = normalizeTransformResults(fetchCache);
344
216
  async function onTransform(filepath) {
345
- if (transformMode === "browser" && project.browser) {
217
+ if (environment === "__browser__" && project.browser) {
346
218
  const result = await project.browser.vite.transformRequest(removeStartsWith(filepath, project.config.root));
347
219
  if (result) {
348
220
  return {
@@ -351,11 +223,11 @@ class V8CoverageProvider extends BaseCoverageProvider {
351
223
  };
352
224
  }
353
225
  }
354
- return project.vitenode.transformRequest(filepath);
226
+ return project.vite.environments[environment].transformRequest(filepath);
355
227
  }
356
228
  const scriptCoverages = [];
357
229
  for (const result of coverage.result) {
358
- if (transformMode === "browser") {
230
+ if (environment === "__browser__") {
359
231
  if (result.url.startsWith("/@fs")) {
360
232
  result.url = `${FILE_PROTOCOL}${removeStartsWith(result.url, "/@fs")}`;
361
233
  } else if (result.url.startsWith(project.config.root)) {
@@ -365,7 +237,10 @@ class V8CoverageProvider extends BaseCoverageProvider {
365
237
  }
366
238
  }
367
239
  if (this.isIncluded(fileURLToPath(result.url))) {
368
- scriptCoverages.push(result);
240
+ scriptCoverages.push({
241
+ ...result,
242
+ url: decodeURIComponent(result.url)
243
+ });
369
244
  }
370
245
  }
371
246
  const coverageMap = this.createCoverageMap();
@@ -382,7 +257,7 @@ class V8CoverageProvider extends BaseCoverageProvider {
382
257
  start = performance.now();
383
258
  timeout = setTimeout(() => debug(c.bgRed(`File "${fileURLToPath(url)}" is taking longer than 3s`)), 3e3);
384
259
  }
385
- const sources = await this.getSources(url, transformResults, onTransform, functions);
260
+ const sources = await this.getSources(url, onTransform, functions);
386
261
  coverageMap.merge(await this.remapCoverage(url, startOffset, sources, functions));
387
262
  if (debug.enabled) {
388
263
  clearTimeout(timeout);
@@ -408,16 +283,6 @@ function findLongestFunctionLength(functions) {
408
283
  return Math.max(previous, maxEndOffset);
409
284
  }, 0);
410
285
  }
411
- function normalizeTransformResults(fetchCache) {
412
- const normalized = new Map();
413
- for (const [key, value] of fetchCache.entries()) {
414
- const cleanEntry = cleanUrl(key);
415
- if (!normalized.has(cleanEntry)) {
416
- normalized.set(cleanEntry, value.result);
417
- }
418
- }
419
- return normalized;
420
- }
421
286
  function removeStartsWith(filepath, start) {
422
287
  if (filepath.startsWith(start)) {
423
288
  return filepath.slice(start.length);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/coverage-v8",
3
3
  "type": "module",
4
- "version": "4.0.0-beta.1",
4
+ "version": "4.0.0-beta.11",
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": "4.0.0-beta.1",
45
- "vitest": "4.0.0-beta.1"
44
+ "@vitest/browser": "4.0.0-beta.11",
45
+ "vitest": "4.0.0-beta.11"
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "@vitest/browser": {
@@ -50,18 +50,17 @@
50
50
  }
51
51
  },
52
52
  "dependencies": {
53
- "@ampproject/remapping": "^2.3.0",
54
53
  "@bcoe/v8-coverage": "^1.0.2",
55
- "ast-v8-to-istanbul": "^0.3.3",
54
+ "ast-v8-to-istanbul": "^0.3.5",
56
55
  "debug": "^4.4.1",
57
56
  "istanbul-lib-coverage": "^3.2.2",
58
57
  "istanbul-lib-report": "^3.0.1",
59
58
  "istanbul-lib-source-maps": "^5.0.6",
60
- "istanbul-reports": "^3.1.7",
61
- "magic-string": "^0.30.17",
59
+ "istanbul-reports": "^3.2.0",
62
60
  "magicast": "^0.3.5",
63
61
  "std-env": "^3.9.0",
64
- "tinyrainbow": "^2.0.0"
62
+ "tinyrainbow": "^3.0.3",
63
+ "@vitest/utils": "4.0.0-beta.11"
65
64
  },
66
65
  "devDependencies": {
67
66
  "@types/debug": "^4.1.12",
@@ -70,9 +69,8 @@
70
69
  "@types/istanbul-lib-source-maps": "^4.0.4",
71
70
  "@types/istanbul-reports": "^3.0.4",
72
71
  "pathe": "^2.0.3",
73
- "vite-node": "4.0.0-beta.1",
74
- "@vitest/browser": "4.0.0-beta.1",
75
- "vitest": "4.0.0-beta.1"
72
+ "@vitest/browser": "4.0.0-beta.11",
73
+ "vitest": "4.0.0-beta.11"
76
74
  },
77
75
  "scripts": {
78
76
  "build": "rimraf dist && rollup -c",