vitest-coverage-merge 0.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/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +141 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/normalize.d.ts +18 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +76 -0
- package/dist/normalize.js.map +1 -0
- package/dist/smart-merge.d.ts +10 -0
- package/dist/smart-merge.d.ts.map +1 -0
- package/dist/smart-merge.js +205 -0
- package/dist/smart-merge.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Steve Zhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# vitest-coverage-merge
|
|
2
|
+
|
|
3
|
+
Merge Vitest coverage from unit tests (jsdom) and browser component tests with automatic normalization.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
When running Vitest with both jsdom (unit tests) and browser mode (component tests), the coverage reports have different statement counts:
|
|
8
|
+
|
|
9
|
+
| Environment | Import Handling |
|
|
10
|
+
|-------------|-----------------|
|
|
11
|
+
| jsdom | V8 doesn't count imports as statements |
|
|
12
|
+
| Real browser | V8 counts imports as executable statements |
|
|
13
|
+
|
|
14
|
+
This makes it impossible to accurately merge coverage without normalization.
|
|
15
|
+
|
|
16
|
+
## The Solution
|
|
17
|
+
|
|
18
|
+
`vitest-coverage-merge` automatically strips import statements and Next.js directives (`'use client'`, `'use server'`) from coverage data before merging, ensuring consistent statement counts across all sources.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -D vitest-coverage-merge
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### CLI
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Merge unit and component coverage
|
|
32
|
+
npx vitest-coverage-merge coverage/unit coverage/component -o coverage/merged
|
|
33
|
+
|
|
34
|
+
# Merge multiple sources
|
|
35
|
+
npx vitest-coverage-merge coverage/unit coverage/component coverage/e2e -o coverage/all
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Options
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
vitest-coverage-merge <dir1> <dir2> [dir3...] -o <output>
|
|
42
|
+
|
|
43
|
+
Arguments:
|
|
44
|
+
<dir1> <dir2> Coverage directories to merge (at least 2 required)
|
|
45
|
+
Each directory should contain coverage-final.json
|
|
46
|
+
|
|
47
|
+
Options:
|
|
48
|
+
-o, --output Output directory for merged coverage (required)
|
|
49
|
+
--no-normalize Skip import/directive stripping (not recommended)
|
|
50
|
+
-h, --help Show help
|
|
51
|
+
-v, --version Show version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Programmatic API
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { mergeCoverage, normalizeCoverage } from 'vitest-coverage-merge'
|
|
58
|
+
|
|
59
|
+
// Merge coverage directories
|
|
60
|
+
const result = await mergeCoverage({
|
|
61
|
+
inputDirs: ['coverage/unit', 'coverage/component'],
|
|
62
|
+
outputDir: 'coverage/merged',
|
|
63
|
+
normalize: true, // default
|
|
64
|
+
reporters: ['json', 'lcov', 'html'], // default
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
console.log(result.statements.pct) // e.g., 85.5
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Example Vitest Setup
|
|
71
|
+
|
|
72
|
+
### vitest.config.ts (unit tests)
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { defineConfig } from 'vitest/config'
|
|
76
|
+
|
|
77
|
+
export default defineConfig({
|
|
78
|
+
test: {
|
|
79
|
+
environment: 'jsdom',
|
|
80
|
+
include: ['src/**/*.test.{ts,tsx}'],
|
|
81
|
+
exclude: ['src/**/*.browser.test.{ts,tsx}'],
|
|
82
|
+
coverage: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
provider: 'v8',
|
|
85
|
+
reportsDirectory: './coverage/unit',
|
|
86
|
+
reporter: ['json', 'lcov', 'html'],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### vitest.component.config.ts (browser tests)
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { defineConfig } from 'vitest/config'
|
|
96
|
+
import { playwright } from '@vitest/browser-playwright'
|
|
97
|
+
|
|
98
|
+
export default defineConfig({
|
|
99
|
+
test: {
|
|
100
|
+
include: ['src/**/*.browser.test.{ts,tsx}'],
|
|
101
|
+
coverage: {
|
|
102
|
+
enabled: true,
|
|
103
|
+
provider: 'v8',
|
|
104
|
+
reportsDirectory: './coverage/component',
|
|
105
|
+
reporter: ['json', 'lcov', 'html'],
|
|
106
|
+
},
|
|
107
|
+
browser: {
|
|
108
|
+
enabled: true,
|
|
109
|
+
provider: playwright(),
|
|
110
|
+
instances: [{ browser: 'chromium' }],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### package.json scripts
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"scripts": {
|
|
121
|
+
"test": "npm run test:unit && npm run test:component",
|
|
122
|
+
"test:unit": "vitest run",
|
|
123
|
+
"test:component": "vitest run --config vitest.component.config.ts",
|
|
124
|
+
"coverage:merge": "vitest-coverage-merge coverage/unit coverage/component -o coverage/merged"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Output
|
|
130
|
+
|
|
131
|
+
The tool generates:
|
|
132
|
+
- `coverage-final.json` - Istanbul coverage data
|
|
133
|
+
- `lcov.info` - LCOV format for CI tools
|
|
134
|
+
- `index.html` - HTML report (in lcov-report folder)
|
|
135
|
+
|
|
136
|
+
## How It Works
|
|
137
|
+
|
|
138
|
+
1. **Load** coverage-final.json from each input directory
|
|
139
|
+
2. **Normalize** by stripping:
|
|
140
|
+
- ESM import statements (`import ... from '...'`)
|
|
141
|
+
- React/Next.js directives (`'use client'`, `'use server'`) - if present
|
|
142
|
+
3. **Smart merge** - selects the best coverage structure (browser tests preferred) and merges execution counts
|
|
143
|
+
4. **Generate** reports (JSON, LCOV, HTML)
|
|
144
|
+
|
|
145
|
+
> **Note**: This tool works with any ESM-based Vitest project (React, Vue, Svelte, vanilla JS/TS, etc.). The React/Next.js directive stripping only applies if those directives are present in your codebase - for non-React projects, it simply has no effect. CommonJS `require()` statements are not stripped because V8 treats them consistently in both jsdom and browser environments.
|
|
146
|
+
|
|
147
|
+
## Why Not Use Vitest's Built-in Merge?
|
|
148
|
+
|
|
149
|
+
Vitest's `--merge-reports` is designed for sharded test runs, not for merging coverage from different environments (jsdom vs browser). It doesn't handle the statement count differences caused by how V8 treats imports differently in each environment.
|
|
150
|
+
|
|
151
|
+
## Related Tools
|
|
152
|
+
|
|
153
|
+
- [nextcov](https://github.com/stevez/nextcov) - E2E coverage collection for Next.js with Playwright
|
|
154
|
+
- [@vitest/coverage-v8](https://www.npmjs.com/package/@vitest/coverage-v8) - V8 coverage provider for Vitest
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import { mergeCoverage } from './index.js';
|
|
5
|
+
function printUsage() {
|
|
6
|
+
console.log(`
|
|
7
|
+
vitest-coverage-merge - Merge Vitest coverage from unit and browser tests
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
vitest-coverage-merge <dir1> <dir2> [dir3...] -o <output>
|
|
11
|
+
|
|
12
|
+
Arguments:
|
|
13
|
+
<dir1> <dir2> Coverage directories to merge (at least 2 required)
|
|
14
|
+
Each directory should contain coverage-final.json
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
-o, --output Output directory for merged coverage (required)
|
|
18
|
+
--no-normalize Skip import/directive stripping (not recommended)
|
|
19
|
+
-h, --help Show this help message
|
|
20
|
+
-v, --version Show version
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
vitest-coverage-merge coverage/unit coverage/component -o coverage/merged
|
|
24
|
+
vitest-coverage-merge coverage/unit coverage/browser coverage/e2e -o coverage/all
|
|
25
|
+
|
|
26
|
+
The tool automatically normalizes coverage by:
|
|
27
|
+
- Stripping import statements (counted differently in jsdom vs browser)
|
|
28
|
+
- Stripping 'use client'/'use server' directives
|
|
29
|
+
- Merging statement, branch, and function coverage
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
function printVersion() {
|
|
33
|
+
console.log('vitest-coverage-merge v0.1.0');
|
|
34
|
+
}
|
|
35
|
+
function parseArgs(args) {
|
|
36
|
+
const result = {
|
|
37
|
+
inputDirs: [],
|
|
38
|
+
outputDir: null,
|
|
39
|
+
normalize: true,
|
|
40
|
+
help: false,
|
|
41
|
+
version: false,
|
|
42
|
+
error: null,
|
|
43
|
+
};
|
|
44
|
+
let i = 0;
|
|
45
|
+
while (i < args.length) {
|
|
46
|
+
const arg = args[i];
|
|
47
|
+
if (arg === '-h' || arg === '--help') {
|
|
48
|
+
result.help = true;
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
if (arg === '-v' || arg === '--version') {
|
|
52
|
+
result.version = true;
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
if (arg === '-o' || arg === '--output') {
|
|
56
|
+
i++;
|
|
57
|
+
if (i >= args.length) {
|
|
58
|
+
result.error = 'Missing output directory after -o/--output';
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
result.outputDir = args[i];
|
|
62
|
+
}
|
|
63
|
+
else if (arg === '--no-normalize') {
|
|
64
|
+
result.normalize = false;
|
|
65
|
+
}
|
|
66
|
+
else if (arg.startsWith('-')) {
|
|
67
|
+
result.error = `Unknown option: ${arg}`;
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
result.inputDirs.push(arg);
|
|
72
|
+
}
|
|
73
|
+
i++;
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
async function main() {
|
|
78
|
+
const args = process.argv.slice(2);
|
|
79
|
+
if (args.length === 0) {
|
|
80
|
+
printUsage();
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
const parsed = parseArgs(args);
|
|
84
|
+
if (parsed.error) {
|
|
85
|
+
console.error(`Error: ${parsed.error}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
if (parsed.help) {
|
|
89
|
+
printUsage();
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
if (parsed.version) {
|
|
93
|
+
printVersion();
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
if (parsed.inputDirs.length < 2) {
|
|
97
|
+
console.error('Error: At least 2 coverage directories are required');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
if (!parsed.outputDir) {
|
|
101
|
+
console.error('Error: Output directory is required (-o <dir>)');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
// Validate input directories
|
|
105
|
+
const validDirs = [];
|
|
106
|
+
const skippedDirs = [];
|
|
107
|
+
for (const dir of parsed.inputDirs) {
|
|
108
|
+
const resolvedDir = resolve(dir);
|
|
109
|
+
const coverageFile = resolve(resolvedDir, 'coverage-final.json');
|
|
110
|
+
if (!existsSync(resolvedDir)) {
|
|
111
|
+
console.log(`Skipped (not found): ${dir}`);
|
|
112
|
+
skippedDirs.push(dir);
|
|
113
|
+
}
|
|
114
|
+
else if (!existsSync(coverageFile)) {
|
|
115
|
+
console.log(`Skipped (no coverage-final.json): ${dir}`);
|
|
116
|
+
skippedDirs.push(dir);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
validDirs.push(resolvedDir);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (validDirs.length < 2) {
|
|
123
|
+
console.error('Error: Need at least 2 valid coverage directories to merge');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const outputDir = resolve(parsed.outputDir);
|
|
127
|
+
try {
|
|
128
|
+
await mergeCoverage({
|
|
129
|
+
inputDirs: validDirs,
|
|
130
|
+
outputDir,
|
|
131
|
+
normalize: parsed.normalize,
|
|
132
|
+
});
|
|
133
|
+
console.log(`\nMerged coverage written to: ${outputDir}`);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
console.error('Error merging coverage:', error);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
main();
|
|
141
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface MergeOptions {
|
|
2
|
+
inputDirs: string[];
|
|
3
|
+
outputDir: string;
|
|
4
|
+
normalize?: boolean;
|
|
5
|
+
reporters?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface MergeResult {
|
|
8
|
+
totalFiles: number;
|
|
9
|
+
statements: {
|
|
10
|
+
covered: number;
|
|
11
|
+
total: number;
|
|
12
|
+
pct: number;
|
|
13
|
+
};
|
|
14
|
+
branches: {
|
|
15
|
+
covered: number;
|
|
16
|
+
total: number;
|
|
17
|
+
pct: number;
|
|
18
|
+
};
|
|
19
|
+
functions: {
|
|
20
|
+
covered: number;
|
|
21
|
+
total: number;
|
|
22
|
+
pct: number;
|
|
23
|
+
};
|
|
24
|
+
lines: {
|
|
25
|
+
covered: number;
|
|
26
|
+
total: number;
|
|
27
|
+
pct: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Merge coverage from multiple Vitest runs.
|
|
32
|
+
*
|
|
33
|
+
* This handles the jsdom vs browser statement count difference by
|
|
34
|
+
* normalizing (stripping imports/directives) before merging.
|
|
35
|
+
*/
|
|
36
|
+
export declare function mergeCoverage(options: MergeOptions): Promise<MergeResult>;
|
|
37
|
+
export { normalizeCoverage } from './normalize.js';
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3D,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CACvD;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA2G/E;AAGD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import libCoverage from 'istanbul-lib-coverage';
|
|
4
|
+
import libReport from 'istanbul-lib-report';
|
|
5
|
+
import reports from 'istanbul-reports';
|
|
6
|
+
import { normalizeCoverage } from './normalize.js';
|
|
7
|
+
import { smartMergeCoverage } from './smart-merge.js';
|
|
8
|
+
/**
|
|
9
|
+
* Merge coverage from multiple Vitest runs.
|
|
10
|
+
*
|
|
11
|
+
* This handles the jsdom vs browser statement count difference by
|
|
12
|
+
* normalizing (stripping imports/directives) before merging.
|
|
13
|
+
*/
|
|
14
|
+
export async function mergeCoverage(options) {
|
|
15
|
+
const { inputDirs, outputDir, normalize = true, reporters = ['json', 'lcov', 'html'], } = options;
|
|
16
|
+
// Load all coverage data
|
|
17
|
+
const coverageMaps = [];
|
|
18
|
+
let totalImportsRemoved = 0;
|
|
19
|
+
let totalDirectivesRemoved = 0;
|
|
20
|
+
for (const dir of inputDirs) {
|
|
21
|
+
const coverageFile = join(dir, 'coverage-final.json');
|
|
22
|
+
if (!existsSync(coverageFile)) {
|
|
23
|
+
console.log(`Skipped (no coverage-final.json): ${dir}`);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
console.log(`Loading: ${coverageFile}`);
|
|
27
|
+
const rawData = readFileSync(coverageFile, 'utf-8');
|
|
28
|
+
let coverageData = JSON.parse(rawData);
|
|
29
|
+
if (normalize) {
|
|
30
|
+
const result = normalizeCoverage(coverageData);
|
|
31
|
+
coverageData = result.coverageMap;
|
|
32
|
+
totalImportsRemoved += result.importsRemoved;
|
|
33
|
+
totalDirectivesRemoved += result.directivesRemoved;
|
|
34
|
+
}
|
|
35
|
+
coverageMaps.push(coverageData);
|
|
36
|
+
}
|
|
37
|
+
if (normalize && (totalImportsRemoved > 0 || totalDirectivesRemoved > 0)) {
|
|
38
|
+
console.log(`Normalized: removed ${totalImportsRemoved} import(s), ${totalDirectivesRemoved} directive(s)`);
|
|
39
|
+
}
|
|
40
|
+
// Smart merge: use source with fewer items as baseline
|
|
41
|
+
const mergedData = smartMergeCoverage(coverageMaps);
|
|
42
|
+
const mergedMap = libCoverage.createCoverageMap(mergedData);
|
|
43
|
+
// Create output directory
|
|
44
|
+
if (!existsSync(outputDir)) {
|
|
45
|
+
mkdirSync(outputDir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
// Write coverage-final.json
|
|
48
|
+
const coverageJson = JSON.stringify(mergedMap.toJSON(), null, 2);
|
|
49
|
+
writeFileSync(join(outputDir, 'coverage-final.json'), coverageJson);
|
|
50
|
+
// Generate reports
|
|
51
|
+
const context = libReport.createContext({
|
|
52
|
+
dir: outputDir,
|
|
53
|
+
defaultSummarizer: 'nested',
|
|
54
|
+
coverageMap: mergedMap,
|
|
55
|
+
});
|
|
56
|
+
// Use Set to avoid duplicate reporters
|
|
57
|
+
const uniqueReporters = [...new Set(reporters)];
|
|
58
|
+
for (const reporter of uniqueReporters) {
|
|
59
|
+
try {
|
|
60
|
+
const report = reports.create(reporter);
|
|
61
|
+
report.execute(context);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.warn(`Warning: Failed to generate ${reporter} report:`, error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Calculate summary
|
|
68
|
+
const summary = mergedMap.getCoverageSummary();
|
|
69
|
+
const result = {
|
|
70
|
+
totalFiles: Object.keys(mergedMap.toJSON()).length,
|
|
71
|
+
statements: {
|
|
72
|
+
covered: summary.statements.covered,
|
|
73
|
+
total: summary.statements.total,
|
|
74
|
+
pct: summary.statements.pct,
|
|
75
|
+
},
|
|
76
|
+
branches: {
|
|
77
|
+
covered: summary.branches.covered,
|
|
78
|
+
total: summary.branches.total,
|
|
79
|
+
pct: summary.branches.pct,
|
|
80
|
+
},
|
|
81
|
+
functions: {
|
|
82
|
+
covered: summary.functions.covered,
|
|
83
|
+
total: summary.functions.total,
|
|
84
|
+
pct: summary.functions.pct,
|
|
85
|
+
},
|
|
86
|
+
lines: {
|
|
87
|
+
covered: summary.lines.covered,
|
|
88
|
+
total: summary.lines.total,
|
|
89
|
+
pct: summary.lines.pct,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
// Print summary
|
|
93
|
+
console.log('\n=============================== Coverage summary ===============================');
|
|
94
|
+
console.log(`Statements : ${result.statements.pct.toFixed(2)}% ( ${result.statements.covered}/${result.statements.total} )`);
|
|
95
|
+
console.log(`Branches : ${result.branches.pct.toFixed(2)}% ( ${result.branches.covered}/${result.branches.total} )`);
|
|
96
|
+
console.log(`Functions : ${result.functions.pct.toFixed(2)}% ( ${result.functions.covered}/${result.functions.total} )`);
|
|
97
|
+
console.log(`Lines : ${result.lines.pct.toFixed(2)}% ( ${result.lines.covered}/${result.lines.total} )`);
|
|
98
|
+
console.log('================================================================================');
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
// Re-export for programmatic use
|
|
102
|
+
export { normalizeCoverage } from './normalize.js';
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CoverageMapData } from 'istanbul-lib-coverage';
|
|
2
|
+
export interface NormalizeResult {
|
|
3
|
+
coverageMap: CoverageMapData;
|
|
4
|
+
importsRemoved: number;
|
|
5
|
+
directivesRemoved: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Normalize coverage data by stripping import statements and Next.js directives.
|
|
9
|
+
*
|
|
10
|
+
* This is necessary because:
|
|
11
|
+
* - jsdom V8 doesn't count import statements as statements
|
|
12
|
+
* - Real browser V8 counts import statements as executable statements
|
|
13
|
+
* - Next.js bundled code has different statement structure
|
|
14
|
+
*
|
|
15
|
+
* By stripping these from all coverage sources, we can merge them accurately.
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeCoverage(coverageMap: CoverageMapData): NormalizeResult;
|
|
18
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,uBAAuB,CAAA;AAE9E,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,eAAe,CAAA;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,eAAe,GAAG,eAAe,CAW/E"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize coverage data by stripping import statements and Next.js directives.
|
|
4
|
+
*
|
|
5
|
+
* This is necessary because:
|
|
6
|
+
* - jsdom V8 doesn't count import statements as statements
|
|
7
|
+
* - Real browser V8 counts import statements as executable statements
|
|
8
|
+
* - Next.js bundled code has different statement structure
|
|
9
|
+
*
|
|
10
|
+
* By stripping these from all coverage sources, we can merge them accurately.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeCoverage(coverageMap) {
|
|
13
|
+
let importsRemoved = 0;
|
|
14
|
+
let directivesRemoved = 0;
|
|
15
|
+
for (const [filePath, fileData] of Object.entries(coverageMap)) {
|
|
16
|
+
const result = normalizeFileCoverage(filePath, fileData);
|
|
17
|
+
importsRemoved += result.importsRemoved;
|
|
18
|
+
directivesRemoved += result.directivesRemoved;
|
|
19
|
+
}
|
|
20
|
+
return { coverageMap, importsRemoved, directivesRemoved };
|
|
21
|
+
}
|
|
22
|
+
function normalizeFileCoverage(filePath, fileData) {
|
|
23
|
+
let importsRemoved = 0;
|
|
24
|
+
let directivesRemoved = 0;
|
|
25
|
+
// Read source file to check line content
|
|
26
|
+
let lines = [];
|
|
27
|
+
try {
|
|
28
|
+
if (existsSync(filePath)) {
|
|
29
|
+
lines = readFileSync(filePath, 'utf-8').split('\n');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// File not found, skip normalization for this file
|
|
34
|
+
return { importsRemoved: 0, directivesRemoved: 0 };
|
|
35
|
+
}
|
|
36
|
+
if (lines.length === 0) {
|
|
37
|
+
return { importsRemoved: 0, directivesRemoved: 0 };
|
|
38
|
+
}
|
|
39
|
+
// Find statement keys to remove
|
|
40
|
+
const keysToRemove = [];
|
|
41
|
+
for (const [key, stmt] of Object.entries(fileData.statementMap || {})) {
|
|
42
|
+
const lineNum = stmt.start.line;
|
|
43
|
+
const lineContent = lines[lineNum - 1]?.trim() || '';
|
|
44
|
+
// Check if line is an import statement
|
|
45
|
+
if (lineContent.startsWith('import ') || lineContent.startsWith('import{')) {
|
|
46
|
+
keysToRemove.push({ key, type: 'import' });
|
|
47
|
+
}
|
|
48
|
+
// Check if line is a 'use server' or 'use client' directive
|
|
49
|
+
else if (isDirective(lineContent)) {
|
|
50
|
+
keysToRemove.push({ key, type: 'directive' });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Remove statements
|
|
54
|
+
for (const { key, type } of keysToRemove) {
|
|
55
|
+
delete fileData.statementMap[key];
|
|
56
|
+
delete fileData.s[key];
|
|
57
|
+
if (type === 'import') {
|
|
58
|
+
importsRemoved++;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
directivesRemoved++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { importsRemoved, directivesRemoved };
|
|
65
|
+
}
|
|
66
|
+
function isDirective(line) {
|
|
67
|
+
return (line === "'use server'" ||
|
|
68
|
+
line === '"use server"' ||
|
|
69
|
+
line === "'use server';" ||
|
|
70
|
+
line === '"use server";' ||
|
|
71
|
+
line === "'use client'" ||
|
|
72
|
+
line === '"use client"' ||
|
|
73
|
+
line === "'use client';" ||
|
|
74
|
+
line === '"use client";');
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CoverageMapData } from 'istanbul-lib-coverage';
|
|
2
|
+
/**
|
|
3
|
+
* Smart merge multiple coverage maps.
|
|
4
|
+
*
|
|
5
|
+
* This uses a "fewer items wins" strategy for structure selection,
|
|
6
|
+
* which preserves the statement counts from the source without
|
|
7
|
+
* import statement inflation.
|
|
8
|
+
*/
|
|
9
|
+
export declare function smartMergeCoverage(coverageMaps: CoverageMapData[]): CoverageMapData;
|
|
10
|
+
//# sourceMappingURL=smart-merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-merge.d.ts","sourceRoot":"","sources":["../src/smart-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,uBAAuB,CAAA;AAuN9E;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,eAAe,CAgCnF"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a unique key for a location (exact match)
|
|
3
|
+
*/
|
|
4
|
+
function locationKey(loc) {
|
|
5
|
+
return `${loc.start.line}:${loc.start.column}`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Get the line number from a location (for line-based fallback matching)
|
|
9
|
+
*/
|
|
10
|
+
function lineKey(loc) {
|
|
11
|
+
return loc.start.line;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build lookup maps from file coverage data for efficient merging.
|
|
15
|
+
*/
|
|
16
|
+
function buildLookups(data) {
|
|
17
|
+
const stmts = new Map();
|
|
18
|
+
const stmtsByLine = new Map();
|
|
19
|
+
for (const [key, loc] of Object.entries(data.statementMap || {})) {
|
|
20
|
+
const count = data.s[key] || 0;
|
|
21
|
+
if (count > 0) {
|
|
22
|
+
stmts.set(locationKey(loc), count);
|
|
23
|
+
const line = lineKey(loc);
|
|
24
|
+
stmtsByLine.set(line, Math.max(stmtsByLine.get(line) || 0, count));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const fns = new Map();
|
|
28
|
+
const fnsByLine = new Map();
|
|
29
|
+
for (const [key, fn] of Object.entries(data.fnMap || {})) {
|
|
30
|
+
const count = data.f[key] || 0;
|
|
31
|
+
if (count > 0) {
|
|
32
|
+
fns.set(locationKey(fn.loc), count);
|
|
33
|
+
const line = lineKey(fn.loc);
|
|
34
|
+
fnsByLine.set(line, Math.max(fnsByLine.get(line) || 0, count));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const branches = new Map();
|
|
38
|
+
const branchesByLine = new Map();
|
|
39
|
+
for (const [key, branch] of Object.entries(data.branchMap || {})) {
|
|
40
|
+
const counts = data.b[key] || [];
|
|
41
|
+
if (counts.some((c) => c > 0)) {
|
|
42
|
+
branches.set(locationKey(branch.loc), counts);
|
|
43
|
+
const line = lineKey(branch.loc);
|
|
44
|
+
if (!branchesByLine.has(line)) {
|
|
45
|
+
branchesByLine.set(line, counts);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { stmts, stmtsByLine, fns, fnsByLine, branches, branchesByLine };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Select the best source coverage for structure.
|
|
53
|
+
* Prefers coverage WITHOUT L1:0 directive statements (browser/E2E-style).
|
|
54
|
+
* Browser coverage is more accurate because it doesn't count non-executable directives.
|
|
55
|
+
*
|
|
56
|
+
* Rules:
|
|
57
|
+
* 1. Filter out sources with no coverage data (0 items)
|
|
58
|
+
* 2. Among remaining sources, prefer those without L1:0 directives
|
|
59
|
+
* 3. Among sources without directives, prefer the LAST one (component tests by convention)
|
|
60
|
+
* 4. If all sources have directives, pick the one with fewer items
|
|
61
|
+
*/
|
|
62
|
+
function selectBestSource(coverages) {
|
|
63
|
+
const getTotalItems = (cov) => {
|
|
64
|
+
return (Object.keys(cov.statementMap || {}).length +
|
|
65
|
+
Object.keys(cov.branchMap || {}).length +
|
|
66
|
+
Object.keys(cov.fnMap || {}).length);
|
|
67
|
+
};
|
|
68
|
+
// Filter out sources with no coverage data at all
|
|
69
|
+
// Preserve original indices for "prefer last" logic
|
|
70
|
+
const nonEmptyWithIndex = coverages
|
|
71
|
+
.map((cov, idx) => ({ cov, idx }))
|
|
72
|
+
.filter(({ cov }) => getTotalItems(cov) > 0);
|
|
73
|
+
if (nonEmptyWithIndex.length === 0) {
|
|
74
|
+
return coverages[0];
|
|
75
|
+
}
|
|
76
|
+
if (nonEmptyWithIndex.length === 1) {
|
|
77
|
+
return nonEmptyWithIndex[0].cov;
|
|
78
|
+
}
|
|
79
|
+
// Check which coverages have L1:0 directive statements
|
|
80
|
+
const withDirective = [];
|
|
81
|
+
const withoutDirective = [];
|
|
82
|
+
for (const item of nonEmptyWithIndex) {
|
|
83
|
+
const hasDirective = Object.values(item.cov.statementMap || {}).some((loc) => {
|
|
84
|
+
const typedLoc = loc;
|
|
85
|
+
return typedLoc.start.line === 1 && (typedLoc.start.column === 0 || typedLoc.start.column === null);
|
|
86
|
+
});
|
|
87
|
+
if (hasDirective) {
|
|
88
|
+
withDirective.push(item);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
withoutDirective.push(item);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Prefer coverage without directive (browser-style)
|
|
95
|
+
if (withoutDirective.length > 0) {
|
|
96
|
+
// Among sources without directives, prefer the LAST one in the original array
|
|
97
|
+
// By convention, component tests are passed last when merging
|
|
98
|
+
const lastItem = withoutDirective.reduce((best, current) => current.idx > best.idx ? current : best);
|
|
99
|
+
return lastItem.cov;
|
|
100
|
+
}
|
|
101
|
+
// All non-empty sources have directives - pick the one with fewer items
|
|
102
|
+
return nonEmptyWithIndex.reduce((best, current) => getTotalItems(current.cov) < getTotalItems(best.cov) ? current : best).cov;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Smart merge of multiple file coverages.
|
|
106
|
+
* Uses the source with fewer items as the baseline structure,
|
|
107
|
+
* then merges execution counts from all sources.
|
|
108
|
+
*/
|
|
109
|
+
function mergeFileCoverages(coverages) {
|
|
110
|
+
if (coverages.length === 0) {
|
|
111
|
+
throw new Error('No coverages to merge');
|
|
112
|
+
}
|
|
113
|
+
if (coverages.length === 1) {
|
|
114
|
+
return JSON.parse(JSON.stringify(coverages[0]));
|
|
115
|
+
}
|
|
116
|
+
// Select best structure (fewer items)
|
|
117
|
+
const bestSource = selectBestSource(coverages);
|
|
118
|
+
// Build lookup maps for all coverages
|
|
119
|
+
const allLookups = coverages.map(buildLookups);
|
|
120
|
+
// Start with best structure (deep copy)
|
|
121
|
+
const merged = {
|
|
122
|
+
path: coverages[0].path,
|
|
123
|
+
statementMap: JSON.parse(JSON.stringify(bestSource.statementMap)),
|
|
124
|
+
s: JSON.parse(JSON.stringify(bestSource.s)),
|
|
125
|
+
fnMap: JSON.parse(JSON.stringify(bestSource.fnMap)),
|
|
126
|
+
f: JSON.parse(JSON.stringify(bestSource.f)),
|
|
127
|
+
branchMap: JSON.parse(JSON.stringify(bestSource.branchMap)),
|
|
128
|
+
b: JSON.parse(JSON.stringify(bestSource.b)),
|
|
129
|
+
};
|
|
130
|
+
// Merge statement counts from all sources
|
|
131
|
+
for (const [key, loc] of Object.entries(merged.statementMap)) {
|
|
132
|
+
const locKey = locationKey(loc);
|
|
133
|
+
const line = lineKey(loc);
|
|
134
|
+
let maxCount = merged.s[key] || 0;
|
|
135
|
+
for (const lookup of allLookups) {
|
|
136
|
+
const count = lookup.stmts.get(locKey) ?? lookup.stmtsByLine.get(line);
|
|
137
|
+
if (count !== undefined && count > maxCount) {
|
|
138
|
+
maxCount = count;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
merged.s[key] = maxCount;
|
|
142
|
+
}
|
|
143
|
+
// Merge function counts from all sources
|
|
144
|
+
for (const [key, fn] of Object.entries(merged.fnMap)) {
|
|
145
|
+
const locKey = locationKey(fn.loc);
|
|
146
|
+
const line = lineKey(fn.loc);
|
|
147
|
+
let maxCount = merged.f[key] || 0;
|
|
148
|
+
for (const lookup of allLookups) {
|
|
149
|
+
const count = lookup.fns.get(locKey) ?? lookup.fnsByLine.get(line);
|
|
150
|
+
if (count !== undefined && count > maxCount) {
|
|
151
|
+
maxCount = count;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
merged.f[key] = maxCount;
|
|
155
|
+
}
|
|
156
|
+
// Merge branch counts from all sources
|
|
157
|
+
for (const [key, branch] of Object.entries(merged.branchMap)) {
|
|
158
|
+
const locKey = locationKey(branch.loc);
|
|
159
|
+
const line = lineKey(branch.loc);
|
|
160
|
+
const baseCounts = merged.b[key] || [];
|
|
161
|
+
for (const lookup of allLookups) {
|
|
162
|
+
const counts = lookup.branches.get(locKey) ?? lookup.branchesByLine.get(line);
|
|
163
|
+
if (counts !== undefined) {
|
|
164
|
+
merged.b[key] = baseCounts.map((c, i) => Math.max(c, counts[i] || 0));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return merged;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Smart merge multiple coverage maps.
|
|
172
|
+
*
|
|
173
|
+
* This uses a "fewer items wins" strategy for structure selection,
|
|
174
|
+
* which preserves the statement counts from the source without
|
|
175
|
+
* import statement inflation.
|
|
176
|
+
*/
|
|
177
|
+
export function smartMergeCoverage(coverageMaps) {
|
|
178
|
+
if (coverageMaps.length === 0) {
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
181
|
+
if (coverageMaps.length === 1) {
|
|
182
|
+
return JSON.parse(JSON.stringify(coverageMaps[0]));
|
|
183
|
+
}
|
|
184
|
+
// Collect all files from all maps
|
|
185
|
+
const allFiles = new Set();
|
|
186
|
+
for (const map of coverageMaps) {
|
|
187
|
+
for (const file of Object.keys(map)) {
|
|
188
|
+
allFiles.add(file);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const merged = {};
|
|
192
|
+
for (const file of allFiles) {
|
|
193
|
+
const fileCoverages = coverageMaps
|
|
194
|
+
.filter((m) => file in m)
|
|
195
|
+
.map((m) => m[file]);
|
|
196
|
+
if (fileCoverages.length === 1) {
|
|
197
|
+
merged[file] = JSON.parse(JSON.stringify(fileCoverages[0]));
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
merged[file] = mergeFileCoverages(fileCoverages);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return merged;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=smart-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-merge.js","sourceRoot":"","sources":["../src/smart-merge.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitest-coverage-merge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Merge Vitest unit and browser component test coverage with automatic normalization",
|
|
5
|
+
"author": "Steve Zhang",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"bin": {
|
|
12
|
+
"vitest-coverage-merge": "./dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"lint": "eslint src",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"vitest",
|
|
33
|
+
"coverage",
|
|
34
|
+
"merge",
|
|
35
|
+
"istanbul",
|
|
36
|
+
"v8",
|
|
37
|
+
"unit-test",
|
|
38
|
+
"component-test",
|
|
39
|
+
"browser-test"
|
|
40
|
+
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"istanbul-lib-coverage": "^3.2.2",
|
|
43
|
+
"istanbul-lib-report": "^3.0.1",
|
|
44
|
+
"istanbul-reports": "^3.1.7"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/istanbul-lib-coverage": "^2.0.6",
|
|
48
|
+
"@types/istanbul-lib-report": "^3.0.3",
|
|
49
|
+
"@types/istanbul-reports": "^3.0.4",
|
|
50
|
+
"@types/node": "^22.10.0",
|
|
51
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
52
|
+
"typescript": "^5.7.0",
|
|
53
|
+
"vitest": "^4.0.15"
|
|
54
|
+
},
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/stevez/vitest-coverage-merge.git"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18"
|
|
61
|
+
}
|
|
62
|
+
}
|