@vitest/coverage-istanbul 1.0.0-beta.5 → 1.0.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/provider.d.ts +7 -10
- package/dist/provider.js +71 -49
- package/package.json +4 -2
package/dist/provider.d.ts
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
import { CoverageProvider, Vitest, AfterSuiteRunMeta, ReportContext, ResolvedCoverageOptions } from 'vitest';
|
2
2
|
import { BaseCoverageProvider } from 'vitest/coverage';
|
3
|
-
import {
|
3
|
+
import { CoverageMap } from 'istanbul-lib-coverage';
|
4
4
|
import { Instrumenter } from 'istanbul-lib-instrument';
|
5
5
|
|
6
6
|
type Options = ResolvedCoverageOptions<'istanbul'>;
|
7
|
-
type
|
7
|
+
type Filename = string;
|
8
|
+
type CoverageFilesByTransformMode = Record<AfterSuiteRunMeta['transformMode'], Filename[]>;
|
8
9
|
type ProjectName = NonNullable<AfterSuiteRunMeta['projectName']> | typeof DEFAULT_PROJECT;
|
9
10
|
interface TestExclude {
|
10
11
|
new (opts: {
|
@@ -26,13 +27,9 @@ declare class IstanbulCoverageProvider extends BaseCoverageProvider implements C
|
|
26
27
|
options: Options;
|
27
28
|
instrumenter: Instrumenter;
|
28
29
|
testExclude: InstanceType<TestExclude>;
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
* If storing in memory causes issues, we can simply write these into fs in `onAfterSuiteRun`
|
33
|
-
* and read them back when merging coverage objects in `onAfterAllFilesRun`.
|
34
|
-
*/
|
35
|
-
coverages: Map<ProjectName, CoverageByTransformMode>;
|
30
|
+
coverageFiles: Map<ProjectName, CoverageFilesByTransformMode>;
|
31
|
+
coverageFilesDirectory: string;
|
32
|
+
pendingPromises: Promise<void>[];
|
36
33
|
initialize(ctx: Vitest): void;
|
37
34
|
resolveOptions(): Options;
|
38
35
|
onFileTransform(sourceCode: string, id: string, pluginCtx: any): {
|
@@ -42,7 +39,7 @@ declare class IstanbulCoverageProvider extends BaseCoverageProvider implements C
|
|
42
39
|
onAfterSuiteRun({ coverage, transformMode, projectName }: AfterSuiteRunMeta): void;
|
43
40
|
clean(clean?: boolean): Promise<void>;
|
44
41
|
reportCoverage({ allTestsRun }?: ReportContext): Promise<void>;
|
45
|
-
getCoverageMapForUncoveredFiles(coveredFiles: string[]): Promise<
|
42
|
+
getCoverageMapForUncoveredFiles(coveredFiles: string[]): Promise<CoverageMap>;
|
46
43
|
}
|
47
44
|
|
48
45
|
export { IstanbulCoverageProvider };
|
package/dist/provider.js
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
import {
|
1
|
+
import { promises, existsSync, writeFileSync } from 'node:fs';
|
2
2
|
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config';
|
3
3
|
import { BaseCoverageProvider } from 'vitest/coverage';
|
4
4
|
import c from 'picocolors';
|
5
5
|
import { parseModule } from 'magicast';
|
6
|
+
import createDebug from 'debug';
|
6
7
|
import libReport from 'istanbul-lib-report';
|
7
8
|
import reports from 'istanbul-reports';
|
8
9
|
import libCoverage from 'istanbul-lib-coverage';
|
@@ -106,19 +107,17 @@ const isAbsolute = function(p) {
|
|
106
107
|
};
|
107
108
|
|
108
109
|
const DEFAULT_PROJECT = Symbol.for("default-project");
|
110
|
+
const debug = createDebug("vitest:coverage");
|
111
|
+
let uniqueId = 0;
|
109
112
|
class IstanbulCoverageProvider extends BaseCoverageProvider {
|
110
113
|
name = "istanbul";
|
111
114
|
ctx;
|
112
115
|
options;
|
113
116
|
instrumenter;
|
114
117
|
testExclude;
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
* If storing in memory causes issues, we can simply write these into fs in `onAfterSuiteRun`
|
119
|
-
* and read them back when merging coverage objects in `onAfterAllFilesRun`.
|
120
|
-
*/
|
121
|
-
coverages = /* @__PURE__ */ new Map();
|
118
|
+
coverageFiles = /* @__PURE__ */ new Map();
|
119
|
+
coverageFilesDirectory;
|
120
|
+
pendingPromises = [];
|
122
121
|
initialize(ctx) {
|
123
122
|
const config = ctx.config.coverage;
|
124
123
|
this.ctx = ctx;
|
@@ -157,6 +156,7 @@ class IstanbulCoverageProvider extends BaseCoverageProvider {
|
|
157
156
|
extension: this.options.extension,
|
158
157
|
relativePath: !this.options.allowExternal
|
159
158
|
});
|
159
|
+
this.coverageFilesDirectory = resolve(this.options.reportsDirectory, ".tmp");
|
160
160
|
}
|
161
161
|
resolveOptions() {
|
162
162
|
return this.options;
|
@@ -176,33 +176,58 @@ class IstanbulCoverageProvider extends BaseCoverageProvider {
|
|
176
176
|
* backwards compatibility is a breaking change.
|
177
177
|
*/
|
178
178
|
onAfterSuiteRun({ coverage, transformMode, projectName }) {
|
179
|
+
if (!coverage)
|
180
|
+
return;
|
179
181
|
if (transformMode !== "web" && transformMode !== "ssr")
|
180
182
|
throw new Error(`Invalid transform mode: ${transformMode}`);
|
181
|
-
let entry = this.
|
183
|
+
let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
|
182
184
|
if (!entry) {
|
183
185
|
entry = { web: [], ssr: [] };
|
184
|
-
this.
|
186
|
+
this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
|
185
187
|
}
|
186
|
-
|
188
|
+
const filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
|
189
|
+
entry[transformMode].push(filename);
|
190
|
+
const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
|
191
|
+
this.pendingPromises.push(promise);
|
187
192
|
}
|
188
193
|
async clean(clean = true) {
|
189
194
|
if (clean && existsSync(this.options.reportsDirectory))
|
190
195
|
await promises.rm(this.options.reportsDirectory, { recursive: true, force: true, maxRetries: 10 });
|
191
|
-
this.
|
196
|
+
if (existsSync(this.coverageFilesDirectory))
|
197
|
+
await promises.rm(this.coverageFilesDirectory, { recursive: true, force: true, maxRetries: 10 });
|
198
|
+
await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
|
199
|
+
this.coverageFiles = /* @__PURE__ */ new Map();
|
200
|
+
this.pendingPromises = [];
|
192
201
|
}
|
193
202
|
async reportCoverage({ allTestsRun } = {}) {
|
194
|
-
const
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
)
|
203
|
+
const coverageMap = libCoverage.createCoverageMap({});
|
204
|
+
let index = 0;
|
205
|
+
const total = this.pendingPromises.length;
|
206
|
+
await Promise.all(this.pendingPromises);
|
207
|
+
this.pendingPromises = [];
|
208
|
+
for (const coveragePerProject of this.coverageFiles.values()) {
|
209
|
+
for (const filenames of [coveragePerProject.ssr, coveragePerProject.web]) {
|
210
|
+
const coverageMapByTransformMode = libCoverage.createCoverageMap({});
|
211
|
+
for (const chunk of toSlices(filenames, this.options.processingConcurrency)) {
|
212
|
+
if (debug.enabled) {
|
213
|
+
index += chunk.length;
|
214
|
+
debug("Covered files %d/%d", index, total);
|
215
|
+
}
|
216
|
+
await Promise.all(chunk.map(async (filename) => {
|
217
|
+
const contents = await promises.readFile(filename, "utf-8");
|
218
|
+
const coverage = JSON.parse(contents);
|
219
|
+
coverageMapByTransformMode.merge(coverage);
|
220
|
+
}));
|
221
|
+
}
|
222
|
+
const transformedCoverage = await transformCoverage(coverageMapByTransformMode);
|
223
|
+
coverageMap.merge(transformedCoverage);
|
224
|
+
}
|
225
|
+
}
|
200
226
|
if (this.options.all && allTestsRun) {
|
201
|
-
const coveredFiles =
|
227
|
+
const coveredFiles = coverageMap.files();
|
202
228
|
const uncoveredCoverage = await this.getCoverageMapForUncoveredFiles(coveredFiles);
|
203
|
-
|
229
|
+
coverageMap.merge(await transformCoverage(uncoveredCoverage));
|
204
230
|
}
|
205
|
-
const coverageMap = mergeCoverageMaps(...coverageMaps);
|
206
231
|
const context = libReport.createContext({
|
207
232
|
dir: this.options.reportsDirectory,
|
208
233
|
coverageMap,
|
@@ -242,43 +267,28 @@ class IstanbulCoverageProvider extends BaseCoverageProvider {
|
|
242
267
|
});
|
243
268
|
}
|
244
269
|
}
|
270
|
+
await promises.rm(this.coverageFilesDirectory, { recursive: true });
|
271
|
+
this.coverageFiles = /* @__PURE__ */ new Map();
|
245
272
|
}
|
246
273
|
async getCoverageMapForUncoveredFiles(coveredFiles) {
|
247
274
|
const includedFiles = await this.testExclude.glob(this.ctx.config.root);
|
248
275
|
const uncoveredFiles = includedFiles.map((file) => resolve(this.ctx.config.root, file)).filter((file) => !coveredFiles.includes(file));
|
249
|
-
const transformResults = await Promise.all(uncoveredFiles.map(async (filename) => {
|
250
|
-
const transformResult = await this.ctx.vitenode.transformRequest(filename);
|
251
|
-
return { transformResult, filename };
|
252
|
-
}));
|
253
276
|
const coverageMap = libCoverage.createCoverageMap({});
|
254
|
-
for (const
|
255
|
-
|
256
|
-
if (
|
257
|
-
this.
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
);
|
262
|
-
const lastCoverage = this.instrumenter.lastFileCoverage();
|
263
|
-
if (lastCoverage)
|
264
|
-
coverageMap.addFileCoverage(lastCoverage);
|
265
|
-
}
|
277
|
+
for (const [index, filename] of uncoveredFiles.entries()) {
|
278
|
+
debug("Uncovered file %s %d/%d", filename, index, uncoveredFiles.length);
|
279
|
+
if (this.ctx.vitenode.fetchCache.has(filename))
|
280
|
+
this.ctx.vitenode.fetchCache.delete(filename);
|
281
|
+
await this.ctx.vitenode.transformRequest(filename);
|
282
|
+
const lastCoverage = this.instrumenter.lastFileCoverage();
|
283
|
+
coverageMap.addFileCoverage(lastCoverage);
|
266
284
|
}
|
267
|
-
return coverageMap
|
285
|
+
return coverageMap;
|
268
286
|
}
|
269
287
|
}
|
270
|
-
async function
|
271
|
-
|
272
|
-
includeImplicitElseBranches(mergedCoverage);
|
288
|
+
async function transformCoverage(coverageMap) {
|
289
|
+
includeImplicitElseBranches(coverageMap);
|
273
290
|
const sourceMapStore = libSourceMaps.createSourceMapStore();
|
274
|
-
return await sourceMapStore.transformCoverage(
|
275
|
-
}
|
276
|
-
function mergeCoverageMaps(...coverageMaps) {
|
277
|
-
return coverageMaps.reduce((coverage, previousCoverageMap) => {
|
278
|
-
const map = libCoverage.createCoverageMap(coverage);
|
279
|
-
map.merge(previousCoverageMap);
|
280
|
-
return map;
|
281
|
-
}, libCoverage.createCoverageMap({}));
|
291
|
+
return await sourceMapStore.transformCoverage(coverageMap);
|
282
292
|
}
|
283
293
|
function removeQueryParameters(filename) {
|
284
294
|
return filename.split("?")[0];
|
@@ -307,5 +317,17 @@ function isEmptyCoverageRange(range) {
|
|
307
317
|
function hasTerminalReporter(reporters) {
|
308
318
|
return reporters.some(([reporter]) => reporter === "text" || reporter === "text-summary" || reporter === "text-lcov" || reporter === "teamcity");
|
309
319
|
}
|
320
|
+
function toSlices(array, size) {
|
321
|
+
return array.reduce((chunks, item) => {
|
322
|
+
const index = Math.max(0, chunks.length - 1);
|
323
|
+
const lastChunk = chunks[index] || [];
|
324
|
+
chunks[index] = lastChunk;
|
325
|
+
if (lastChunk.length >= size)
|
326
|
+
chunks.push([item]);
|
327
|
+
else
|
328
|
+
lastChunk.push(item);
|
329
|
+
return chunks;
|
330
|
+
}, []);
|
331
|
+
}
|
310
332
|
|
311
333
|
export { IstanbulCoverageProvider };
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitest/coverage-istanbul",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.0.0
|
4
|
+
"version": "1.0.0",
|
5
5
|
"description": "Istanbul coverage provider for Vitest",
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
7
7
|
"license": "MIT",
|
@@ -40,6 +40,7 @@
|
|
40
40
|
"vitest": "^1.0.0-0"
|
41
41
|
},
|
42
42
|
"dependencies": {
|
43
|
+
"debug": "^4.3.4",
|
43
44
|
"istanbul-lib-coverage": "^3.2.2",
|
44
45
|
"istanbul-lib-instrument": "^6.0.1",
|
45
46
|
"istanbul-lib-report": "^3.0.1",
|
@@ -50,13 +51,14 @@
|
|
50
51
|
"test-exclude": "^6.0.0"
|
51
52
|
},
|
52
53
|
"devDependencies": {
|
54
|
+
"@types/debug": "^4.1.12",
|
53
55
|
"@types/istanbul-lib-coverage": "^2.0.6",
|
54
56
|
"@types/istanbul-lib-instrument": "^1.7.7",
|
55
57
|
"@types/istanbul-lib-report": "^3.0.3",
|
56
58
|
"@types/istanbul-lib-source-maps": "^4.0.4",
|
57
59
|
"@types/istanbul-reports": "^3.0.4",
|
58
60
|
"pathe": "^1.1.1",
|
59
|
-
"vitest": "1.0.0
|
61
|
+
"vitest": "1.0.0"
|
60
62
|
},
|
61
63
|
"scripts": {
|
62
64
|
"build": "rimraf dist && rollup -c",
|