monocart-coverage-reports 2.12.10 → 2.12.12
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/README.md +128 -18
- package/README.zh-Hans.md +124 -14
- package/lib/assets.js +1 -1
- package/lib/cli.js +16 -8
- package/lib/converter/converter.js +28 -13
- package/lib/index.d.ts +9 -9
- package/lib/packages/monocart-coverage-assets.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +16 -16
- package/lib/platform/share.js +18 -10
- package/lib/reports/console-details.js +1 -34
- package/lib/utils/gc.js +13 -4
- package/lib/utils/merge/merge.js +5 -9
- package/lib/utils/util.js +55 -104
- package/lib/v8/v8.js +11 -12
- package/package.json +136 -135
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* [Filtering Results](#filtering-results)
|
|
27
27
|
* [Resolve `sourcePath` for the Source Files](#resolve-sourcepath-for-the-source-files)
|
|
28
28
|
* [Adding Empty Coverage for Untested Files](#adding-empty-coverage-for-untested-files)
|
|
29
|
-
* [
|
|
29
|
+
* [Hooks](#hooks)
|
|
30
30
|
* [Ignoring Uncovered Codes](#ignoring-uncovered-codes)
|
|
31
31
|
* [Multiprocessing Support](#multiprocessing-support)
|
|
32
32
|
* [Command Line](#command-line)
|
|
@@ -89,6 +89,8 @@ import { CoverageReport } from 'monocart-coverage-reports';
|
|
|
89
89
|
const mcr = new CoverageReport();
|
|
90
90
|
await mcr.loadConfig();
|
|
91
91
|
```
|
|
92
|
+
> Both `MCR({...})` and `new CoverageReport()` create the same instance. `MCR()` is a shorthand factory function; use `new CoverageReport()` when you prefer class instantiation or need to call `loadConfig()` separately.
|
|
93
|
+
|
|
92
94
|
For more information, see [Multiprocessing Support](#multiprocessing-support)
|
|
93
95
|
|
|
94
96
|
- CLI
|
|
@@ -102,9 +104,38 @@ For more information, see [Command Line](#command-line)
|
|
|
102
104
|
- Options declaration see `CoverageReportOptions` [lib/index.d.ts](./lib/index.d.ts)
|
|
103
105
|
- [Config file](#config-file)
|
|
104
106
|
|
|
107
|
+
| Option | Type | Default | Description |
|
|
108
|
+
| :-- | :-- | :-- | :-- |
|
|
109
|
+
| `logging` | `"off" \| "error" \| "info" \| "debug"` | `"info"` | Logging level. Use `"debug"` to keep raw cache and dump sourcemaps for troubleshooting. See [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap). |
|
|
110
|
+
| `name` | `string` | `"Coverage Report"` | Report title shown in the UI and summary reports. |
|
|
111
|
+
| `reports` | `string \| (string \| ReportDescription)[]` | `""` (auto) | Reports to generate. Defaults to `"v8"` for V8 data and `"html"` for Istanbul data. See [Available Reports](#available-reports). |
|
|
112
|
+
| `outputDir` | `string` | `"./coverage-reports"` | Output directory for all reports and the `.cache` folder. |
|
|
113
|
+
| `inputDir` | `string \| string[]` | `null` | Input directories of raw coverage data for [merging](#merge-coverage-reports). |
|
|
114
|
+
| `baseDir` | `string` | `process.cwd()` | Base dir used to normalize relative source paths. Set when the report is generated from a different working directory than the sources. |
|
|
115
|
+
| `dataDir` | `string` | `null` | Coverage data directory loaded automatically in `generate()` — alternative to `addFromDir()`. |
|
|
116
|
+
| `entryFilter` | `string \| object \| function` | `null` | (V8 only) Filter V8 entries by url. See [Filtering Results](#filtering-results). |
|
|
117
|
+
| `sourceFilter` | `string \| object \| function` | `null` | (V8 only) Filter sources unpacked from sourcemaps. |
|
|
118
|
+
| `filter` | `string \| object \| function` | `null` | Combined filter replacing both `entryFilter` and `sourceFilter`. |
|
|
119
|
+
| `sourcePath` | `object \| function` | `null` | Rewrite source paths; see [Resolve `sourcePath`](#resolve-sourcepath-for-the-source-files). |
|
|
120
|
+
| `all` | `string \| string[] \| object` | `null` | Include untested files as empty coverage. See [Adding Empty Coverage](#adding-empty-coverage-for-untested-files). |
|
|
121
|
+
| `outputFile` | `string` | `"index.html"` | (V8 only) Output `[sub dir/]filename` for the V8 report. |
|
|
122
|
+
| `inline` | `boolean` | `false` | (V8 only) Inline all assets into a single HTML file. |
|
|
123
|
+
| `assetsPath` | `string` | `"./assets"` | (V8 only) Assets directory when `inline` is false. |
|
|
124
|
+
| `lcov` | `boolean` | `false` | Also generate `lcov.info` (equivalent to adding the `lcovonly` report). |
|
|
125
|
+
| `v8Ignore` | `boolean` | `true` | (V8 only) Enable/disable comment-based ignoring (`v8 ignore …`). See [Ignoring Uncovered Codes](#ignoring-uncovered-codes). |
|
|
126
|
+
| `reportPath` | `string \| () => string` | `null` | Override the report entry path (defaults to `outputDir/index.html`). Useful when multiple reports share an `outputDir`. |
|
|
127
|
+
| `watermarks` | `[number, number] \| object` | `[50, 80]` | Low/high thresholds (percent). Accepts per-metric object like `{ bytes:[50,80], lines:[50,80] }`. |
|
|
128
|
+
| `clean` | `boolean` | `true` | Clean previous reports in `outputDir` before generating (the `.cache` dir is preserved unless `cleanCache` is also `true`). |
|
|
129
|
+
| `cleanCache` | `boolean` | `false` | Clean the cache dir on start. When both `clean` and `cleanCache` are `false`, cached coverage data from previous runs is preserved and can be reused across processes. |
|
|
130
|
+
| `gc` | `number` | `null` | Memory threshold in MB; force GC at critical stages when RSS exceeds it. Useful for very large data sets, see [JavaScript heap out of memory](#javascript-heap-out-of-memory). |
|
|
131
|
+
| `sourceMap` | `boolean` | `false` | Save source/sourcemap files to cache for debugging (requires `logging: "debug"`). |
|
|
132
|
+
| `sourceMapResolver` | `(url, defaultResolver) => Promise<string \| object>` | `null` | Custom sourcemap loader (e.g. from a build cache). Call `defaultResolver(url)` to fall back. |
|
|
133
|
+
| `onEntry` | `(entry) => void \| Promise<void>` | `null` | (V8 only) Per-entry hook; see [Hooks](#hooks). |
|
|
134
|
+
| `onEnd` | `(coverageResults) => void \| Promise<void>` | `null` | Hook invoked after report generation; see [Hooks](#hooks). |
|
|
135
|
+
|
|
105
136
|
## Available Reports
|
|
106
137
|
|
|
107
|
-
> V8
|
|
138
|
+
> V8 built-in reports (V8 data only):
|
|
108
139
|
|
|
109
140
|
- `v8`
|
|
110
141
|
- Features:
|
|
@@ -124,7 +155,7 @@ For more information, see [Command Line](#command-line)
|
|
|
124
155
|
|
|
125
156
|

|
|
126
157
|
|
|
127
|
-
> Istanbul
|
|
158
|
+
> Istanbul built-in reports (both V8 and Istanbul data):
|
|
128
159
|
|
|
129
160
|
- `clover`
|
|
130
161
|
- `cobertura`
|
|
@@ -144,7 +175,7 @@ For more information, see [Command Line](#command-line)
|
|
|
144
175
|
- `text-lcov`
|
|
145
176
|
- `text-summary`
|
|
146
177
|
|
|
147
|
-
> Other
|
|
178
|
+
> Other built-in reports (both V8 and Istanbul data):
|
|
148
179
|
|
|
149
180
|
- `codecov` Save coverage data to a json file with [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format) format (defaults to `codecov.json`), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports).
|
|
150
181
|
|
|
@@ -192,13 +223,18 @@ cat path-to/coverage-summary.md >> $GITHUB_STEP_SUMMARY
|
|
|
192
223
|
- V8 custom reporter
|
|
193
224
|
> example: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
|
|
194
225
|
|
|
226
|
+
The `type` option determines which coverage format the reporter receives:
|
|
227
|
+
- `'istanbul'` — the reporter receives Istanbul-format coverage data (`coverageResults.istanbulData`).
|
|
228
|
+
- `'v8'` — the reporter receives native V8-format coverage data (`coverageResults.v8Data`).
|
|
229
|
+
- `'both'` — the reporter receives both formats, accessible via `coverageResults.v8Data` and `coverageResults.istanbulData`.
|
|
230
|
+
|
|
195
231
|
### Multiple Reports:
|
|
196
232
|
```js
|
|
197
233
|
const MCR = require('monocart-coverage-reports');
|
|
198
234
|
const coverageOptions = {
|
|
199
235
|
outputDir: './coverage-reports',
|
|
200
236
|
reports: [
|
|
201
|
-
//
|
|
237
|
+
// built-in reports
|
|
202
238
|
['console-summary'],
|
|
203
239
|
['v8'],
|
|
204
240
|
['html', {
|
|
@@ -464,7 +500,7 @@ export interface CoverageRange {
|
|
|
464
500
|
* @functionName can be an empty string.
|
|
465
501
|
* @ranges is always non-empty. The first range is called the "root range".
|
|
466
502
|
* @isBlockCoverage indicates if the function has block coverage information.
|
|
467
|
-
If this is false, it usually means that the
|
|
503
|
+
If this is false, it usually means that the function was never called.
|
|
468
504
|
It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
|
|
469
505
|
*/
|
|
470
506
|
export interface FunctionCoverage {
|
|
@@ -495,7 +531,22 @@ export type V8CoverageData = ScriptCoverage[];
|
|
|
495
531
|
| Firefox (2%) | ❌ | |
|
|
496
532
|
| Node.js | ✅ | |
|
|
497
533
|
| Deno | ❌ | [issue](https://github.com/denoland/deno/issues/23359) |
|
|
498
|
-
| Bun | ❌ |
|
|
534
|
+
| Bun | ❌ | [issue](https://github.com/oven-sh/bun/issues/30968) |
|
|
535
|
+
|
|
536
|
+
> MCR extends each V8 coverage entry with additional fields:
|
|
537
|
+
|
|
538
|
+
| Field | Type | Description |
|
|
539
|
+
| :-- | :-- | :-- |
|
|
540
|
+
| `url` | `string` | Script URL or file path |
|
|
541
|
+
| `type` | `"js" \| "css"` | Entry type |
|
|
542
|
+
| `source` | `string` | (JS) Source code text |
|
|
543
|
+
| `sourceMap` | `object` | (JS) Parsed sourcemap object |
|
|
544
|
+
| `scriptOffset` | `number` | (JS) Byte offset for the script wrapper (e.g. in `vm` modules). This offset is automatically added to all range start/end values |
|
|
545
|
+
| `distFile` | `string` | (JS) The dist bundle file this source was unpacked from |
|
|
546
|
+
| `empty` | `boolean` | Marked as `true` when this entry was added via the `all` option (no real coverage data), showing 0% coverage |
|
|
547
|
+
| `fake` | `boolean` | Marked as `true` when the source code is not original (e.g. compiled from `.ts`/`.jsx`). The AST parser skips coverage extraction for fake sources |
|
|
548
|
+
|
|
549
|
+
These fields can be read/mutated in [`onEntry`](#hooks) hooks.
|
|
499
550
|
|
|
500
551
|
## Filtering Results
|
|
501
552
|
### Using `entryFilter` and `sourceFilter` to filter the results for V8 report
|
|
@@ -572,7 +623,7 @@ const coverageOptions = {
|
|
|
572
623
|
filter: {
|
|
573
624
|
'**/node_modules/**': false,
|
|
574
625
|
'**/vendor.js': false,
|
|
575
|
-
'**/src/**': true
|
|
626
|
+
'**/src/**': true,
|
|
576
627
|
'**/**': true
|
|
577
628
|
}
|
|
578
629
|
};
|
|
@@ -672,8 +723,10 @@ const coverageOptions = {
|
|
|
672
723
|
};
|
|
673
724
|
```
|
|
674
725
|
|
|
675
|
-
##
|
|
676
|
-
|
|
726
|
+
## Hooks
|
|
727
|
+
|
|
728
|
+
### `onEnd` — after the report is generated
|
|
729
|
+
Typical use case: enforce coverage thresholds.
|
|
677
730
|
```js
|
|
678
731
|
const EC = require('eight-colors');
|
|
679
732
|
const coverageOptions = {
|
|
@@ -703,6 +756,29 @@ const coverageOptions = {
|
|
|
703
756
|
}
|
|
704
757
|
```
|
|
705
758
|
|
|
759
|
+
### `onEntry` — before each V8 entry is processed (V8 only)
|
|
760
|
+
Use this to mutate the entry (for example, transform non-standard sources into valid ECMAScript before the AST parser runs). See [Unparsable source](#unparsable-source).
|
|
761
|
+
```js
|
|
762
|
+
const coverageOptions = {
|
|
763
|
+
onEntry: async (entry) => {
|
|
764
|
+
// entry.source, entry.sourceMap, entry.fake, ...
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### `onStart` / `onReady` — CLI-only
|
|
770
|
+
Only available when using `mcr <command>`:
|
|
771
|
+
- `onStart(coverageReport)` — runs before the child process is spawned.
|
|
772
|
+
- `onReady(coverageReport, nodeV8CoverageDir, subprocess)` — runs after the child exits, before MCR reads coverage from `nodeV8CoverageDir`. Useful when the child hasn't finished flushing coverage data and needs to be waited on.
|
|
773
|
+
```js
|
|
774
|
+
// mcr.config.js
|
|
775
|
+
module.exports = {
|
|
776
|
+
onReady: async (coverageReport, nodeV8CoverageDir, subprocess) => {
|
|
777
|
+
// wait for something, or pre-process the raw files
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
```
|
|
781
|
+
|
|
706
782
|
## Ignoring Uncovered Codes
|
|
707
783
|
To ignore codes, use the special comment which starts with `v8 ignore `:
|
|
708
784
|
- Ignoring all until stop
|
|
@@ -715,9 +791,9 @@ function uncovered() {
|
|
|
715
791
|
- Ignoring the next line or next N lines
|
|
716
792
|
```js
|
|
717
793
|
/* v8 ignore next */
|
|
718
|
-
const os = platform === '
|
|
794
|
+
const os = platform === 'win32' ? 'Windows' : 'Other';
|
|
719
795
|
|
|
720
|
-
const os = platform === '
|
|
796
|
+
const os = platform === 'win32' ? 'Windows' /* v8 ignore next */ : 'Other';
|
|
721
797
|
|
|
722
798
|
// v8 ignore next 3
|
|
723
799
|
if (platform === 'linux') {
|
|
@@ -736,6 +812,7 @@ function uncovered() {
|
|
|
736
812
|
}
|
|
737
813
|
/* node:coverage enable */
|
|
738
814
|
```
|
|
815
|
+
- To disable comment-based ignoring entirely, set `v8Ignore: false`.
|
|
739
816
|
|
|
740
817
|
## Multiprocessing Support
|
|
741
818
|
> The data will be added to `[outputDir]/.cache`, After the generation of the report, this data will be removed unless debugging has been enabled or a raw report has been used, see [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
|
|
@@ -790,13 +867,41 @@ npx mcr node ./test/specs/node.test.js -r v8,console-details --lcov
|
|
|
790
867
|
```
|
|
791
868
|
|
|
792
869
|
- CLI Options
|
|
793
|
-
|
|
870
|
+
|
|
871
|
+
The options below are only available when using the CLI (`mcr <command>`):
|
|
872
|
+
|
|
873
|
+
| Option | Type | Default | Description |
|
|
874
|
+
| :-- | :-- | :-- | :-- |
|
|
875
|
+
| `-c, --config` | `string` | — | Custom config file path |
|
|
876
|
+
| `--import` | `string` | `null` | Preload ESM module via `NODE_OPTIONS`, e.g. `tsx` for TypeScript. Requires Node.js `>= 18.19` |
|
|
877
|
+
| `--require` | `string` | `null` | Preload CJS module via `NODE_OPTIONS` |
|
|
878
|
+
| `--env` | `string` | `null` | Load environment variables from a dotenv file (defaults to `.env`). Requires Node.js `>= 20.6` |
|
|
879
|
+
| `onStart` | `(coverageReport) => Promise<void>` | `null` | Runs before the child process is spawned; see [Hooks](#hooks) |
|
|
880
|
+
| `onReady` | `(coverageReport, nodeV8CoverageDir, subprocess) => Promise<void>` | `null` | Runs after the child exits, before MCR reads coverage; see [Hooks](#hooks) |
|
|
881
|
+
|
|
882
|
+
For other options (e.g. `--name`, `--reports`, `--outputDir`, `--lcov`, etc.), see the [Options](#options) table — they work the same way in CLI.
|
|
794
883
|
|
|
795
884
|
- Use `--` to separate sub CLI args
|
|
796
885
|
```sh
|
|
797
886
|
mcr -c mcr.config.js -- sub-cli -c sub-cli.config.js
|
|
798
887
|
```
|
|
799
888
|
|
|
889
|
+
- `--import <module>` / `--require <module>`: forward a preload module to the child process via `NODE_OPTIONS`. Use this for TypeScript/JSX runtimes (`tsx`, `ts-node`, etc.) and also when loading a `mcr.config.ts`. Requires Node.js `>= 18.19`.
|
|
890
|
+
```sh
|
|
891
|
+
mcr --import tsx node ./test.ts
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
- `--env [path]`: load environment variables from a dotenv file before the child starts (defaults to `.env`). Requires `process.loadEnvFile`, added in Node.js `20.6`.
|
|
895
|
+
```sh
|
|
896
|
+
mcr --env .env.test node ./test.js
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
- `merge`: merge raw coverage data from `inputDir`(s) and generate merged reports directly, without running a child process.
|
|
900
|
+
```sh
|
|
901
|
+
mcr merge -r v8,console-details --inputDir ./coverage-reports/unit/raw,./coverage-reports/e2e/raw --outputDir ./coverage-reports/merged
|
|
902
|
+
```
|
|
903
|
+
See [Merge Coverage Reports](#merge-coverage-reports).
|
|
904
|
+
|
|
800
905
|
- Examples
|
|
801
906
|
- [Mocha](#mocha)
|
|
802
907
|
- [TypeScript](#typescript)
|
|
@@ -811,7 +916,12 @@ Loading config file by priority:
|
|
|
811
916
|
- `mcr.config.cjs`
|
|
812
917
|
- `mcr.config.mjs`
|
|
813
918
|
- `mcr.config.json` - json format
|
|
814
|
-
- `mcr.config.ts` (requires preloading the
|
|
919
|
+
- `mcr.config.ts` (requires preloading the TypeScript execution module, for example with `tsx`):
|
|
920
|
+
```sh
|
|
921
|
+
mcr --import tsx -c mcr.config.ts node ./test.js
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
> If no custom path is given, MCR walks up from the current working directory to find the default config file (via [`find-up`](https://github.com/sindresorhus/find-up)). Files outside the search path will be ignored.
|
|
815
925
|
|
|
816
926
|
## Merge Coverage Reports
|
|
817
927
|
The following usage scenarios may require merging coverage reports:
|
|
@@ -890,7 +1000,7 @@ const coverageOptions = {
|
|
|
890
1000
|
],
|
|
891
1001
|
|
|
892
1002
|
onEnd: () => {
|
|
893
|
-
// remove the raw files if
|
|
1003
|
+
// remove the raw files if no longer needed
|
|
894
1004
|
// inputDir.forEach((p) => {
|
|
895
1005
|
// fs.rmSync(p, {
|
|
896
1006
|
// recursive: true,
|
|
@@ -926,8 +1036,8 @@ In the generated code, there is a position `p`, and we need to find out its corr
|
|
|
926
1036
|
- Further understanding of sourcemap, try [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
|
|
927
1037
|
|
|
928
1038
|
How `MCR` Works:
|
|
929
|
-
|
|
930
|
-
|
|
1039
|
+
1. Tries to fix the original position with string comparison and [`diff-sequences`](https://github.com/jestjs/jest/tree/main/packages/diff-sequences). However, for non-JS code such as Vue templates or JSX, a perfect solution may not always be possible.
|
|
1040
|
+
2. Finds all functions, statements and branches by parsing the source code [AST](https://github.com/acornjs/acorn). (One small caveat: V8 cannot provide effective branch coverage information for `AssignmentPattern`.)
|
|
931
1041
|
|
|
932
1042
|
|
|
933
1043
|
### Unparsable source
|
|
@@ -971,7 +1081,7 @@ const coverageOptions = {
|
|
|
971
1081
|
]
|
|
972
1082
|
};
|
|
973
1083
|
```
|
|
974
|
-
When `logging` is `debug`, the raw report data
|
|
1084
|
+
When `logging` is `debug`, the raw report data is preserved in `[outputDir]/.cache` (or `[outputDir]/raw` if the `raw` report is used). The dist file is also kept in the V8 list, so you can open the browser's devtools and visually verify the coverage data.
|
|
975
1085
|

|
|
976
1086
|
|
|
977
1087
|
- Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
|
package/README.zh-Hans.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* [过滤V8覆盖率数据](#filtering-results)
|
|
27
27
|
* [使用 `sourcePath` 修改源文件路径](#resolve-sourcepath-for-the-source-files)
|
|
28
28
|
* [为未测试的文件添加空的覆盖率报告](#adding-empty-coverage-for-untested-files)
|
|
29
|
-
* [
|
|
29
|
+
* [回调钩子 Hooks](#hooks)
|
|
30
30
|
* [如何忽略未覆盖的代码](#ignoring-uncovered-codes)
|
|
31
31
|
* [多进程支持](#multiprocessing-support)
|
|
32
32
|
* [如何使用CLI命令行](#command-line)
|
|
@@ -89,6 +89,8 @@ import { CoverageReport } from 'monocart-coverage-reports';
|
|
|
89
89
|
const mcr = new CoverageReport();
|
|
90
90
|
await mcr.loadConfig();
|
|
91
91
|
```
|
|
92
|
+
> `MCR({...})` 和 `new CoverageReport()` 创建的是同一个实例。`MCR()` 是简写工厂函数;`new CoverageReport()` 更适合需要单独调用 `loadConfig()` 的场景。
|
|
93
|
+
|
|
92
94
|
参见 [多进程支持](#multiprocessing-support)
|
|
93
95
|
|
|
94
96
|
- CLI
|
|
@@ -102,6 +104,35 @@ mcr node my-app.js -r v8,console-details
|
|
|
102
104
|
- 选项的类型描述,见 `CoverageReportOptions` [lib/index.d.ts](./lib/index.d.ts)
|
|
103
105
|
- [配置文件](#config-file)
|
|
104
106
|
|
|
107
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
108
|
+
| :-- | :-- | :-- | :-- |
|
|
109
|
+
| `logging` | `"off" \| "error" \| "info" \| "debug"` | `"info"` | 日志级别。设为 `"debug"` 会保留原始缓存并输出 sourcemap,便于排查问题。参见 [调试覆盖率和sourcemap](#debug-for-coverage-and-sourcemap)。 |
|
|
110
|
+
| `name` | `string` | `"Coverage Report"` | 报告标题,显示在 UI 以及 summary 报告中。 |
|
|
111
|
+
| `reports` | `string \| (string \| ReportDescription)[]` | `""` (自动) | 生成哪些报告。V8 数据默认 `"v8"`,Istanbul 数据默认 `"html"`。参见 [所有支持的报告类型](#available-reports)。 |
|
|
112
|
+
| `outputDir` | `string` | `"./coverage-reports"` | 所有报告以及 `.cache` 的输出目录。 |
|
|
113
|
+
| `inputDir` | `string \| string[]` | `null` | 导入的原始覆盖率数据目录(用于 [合并](#merge-coverage-reports))。 |
|
|
114
|
+
| `baseDir` | `string` | `process.cwd()` | 规范化相对源路径的基准目录。当生成报告的工作目录和源码目录不一致时需要设置。 |
|
|
115
|
+
| `dataDir` | `string` | `null` | `generate()` 阶段自动加载的覆盖率数据目录,作为 `addFromDir()` 的替代。 |
|
|
116
|
+
| `entryFilter` | `string \| object \| function` | `null` | (仅 V8)按 URL 过滤入口文件,参见 [过滤V8覆盖率数据](#filtering-results)。 |
|
|
117
|
+
| `sourceFilter` | `string \| object \| function` | `null` | (仅 V8)过滤从 sourcemap 解包出的源文件。 |
|
|
118
|
+
| `filter` | `string \| object \| function` | `null` | 合并 `entryFilter` 和 `sourceFilter` 的统一过滤器。 |
|
|
119
|
+
| `sourcePath` | `object \| function` | `null` | 修改源文件路径,参见 [使用 `sourcePath`](#resolve-sourcepath-for-the-source-files)。 |
|
|
120
|
+
| `all` | `string \| string[] \| object` | `null` | 为未测试文件添加空覆盖率,参见 [为未测试的文件添加空的覆盖率报告](#adding-empty-coverage-for-untested-files)。 |
|
|
121
|
+
| `outputFile` | `string` | `"index.html"` | (仅 V8)V8 报告的 `[子目录/]文件名`。 |
|
|
122
|
+
| `inline` | `boolean` | `false` | (仅 V8)将所有资源内联到单一 HTML 文件。 |
|
|
123
|
+
| `assetsPath` | `string` | `"./assets"` | (仅 V8)非内联模式下的资源目录。 |
|
|
124
|
+
| `lcov` | `boolean` | `false` | 额外生成 `lcov.info`,等同于添加 `lcovonly` 报告。 |
|
|
125
|
+
| `v8Ignore` | `boolean` | `true` | (仅 V8)启用/禁用基于注释的忽略(`v8 ignore …`),参见 [忽略未覆盖代码](#ignoring-uncovered-codes)。 |
|
|
126
|
+
| `reportPath` | `string \| () => string` | `null` | 覆盖默认的报告入口路径(默认 `outputDir/index.html`)。多个报告共用同一 `outputDir` 时很有用。 |
|
|
127
|
+
| `watermarks` | `[number, number] \| object` | `[50, 80]` | 覆盖率高/低阈值百分比。也支持按指标配置:`{ bytes:[50,80], lines:[50,80] }`。 |
|
|
128
|
+
| `clean` | `boolean` | `true` | 生成报告前清理 `outputDir` 中的旧报告(`.cache` 目录在 `cleanCache` 也为 `true` 时才会被清理)。 |
|
|
129
|
+
| `cleanCache` | `boolean` | `false` | 启动时清理缓存目录。当 `clean` 和 `cleanCache` 都为 `false` 时,之前运行的缓存数据会保留,可在多进程间复用。 |
|
|
130
|
+
| `gc` | `number` | `null` | 内存阈值(MB),在关键阶段若 RSS 超过阈值则强制触发 GC,适用于超大数据集,参见 [JavaScript heap out of memory](#javascript-heap-out-of-memory)。 |
|
|
131
|
+
| `sourceMap` | `boolean` | `false` | 将源文件和 sourcemap 也保存到 cache 便于调试(需要 `logging: "debug"`)。 |
|
|
132
|
+
| `sourceMapResolver` | `(url, defaultResolver) => Promise<string \| object>` | `null` | 自定义 sourcemap 加载逻辑(例如从构建缓存中读取),可调用 `defaultResolver(url)` 回退到默认解析。 |
|
|
133
|
+
| `onEntry` | `(entry) => void \| Promise<void>` | `null` | (仅 V8)每个入口文件处理前触发,参见 [Hooks](#hooks)。 |
|
|
134
|
+
| `onEnd` | `(coverageResults) => void \| Promise<void>` | `null` | 报告生成完成后触发,参见 [Hooks](#hooks)。 |
|
|
135
|
+
|
|
105
136
|
## Available Reports
|
|
106
137
|
|
|
107
138
|
> 内置V8报告(仅V8格式数据支持):
|
|
@@ -192,6 +223,11 @@ cat path-to/coverage-summary.md >> $GITHUB_STEP_SUMMARY
|
|
|
192
223
|
- V8自定义报告
|
|
193
224
|
> 例子: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
|
|
194
225
|
|
|
226
|
+
`type` 选项决定报告接收的覆盖率数据格式:
|
|
227
|
+
- `'istanbul'` — 报告接收 Istanbul 格式的覆盖率数据(`coverageResults.istanbulData`)。
|
|
228
|
+
- `'v8'` — 报告接收原生 V8 格式的覆盖率数据(`coverageResults.v8Data`)。
|
|
229
|
+
- `'both'` — 报告同时接收两种格式,可通过 `coverageResults.v8Data` 和 `coverageResults.istanbulData` 访问。
|
|
230
|
+
|
|
195
231
|
### Multiple Reports:
|
|
196
232
|
如何配置多个报告
|
|
197
233
|
```js
|
|
@@ -199,7 +235,7 @@ const MCR = require('monocart-coverage-reports');
|
|
|
199
235
|
const coverageOptions = {
|
|
200
236
|
outputDir: './coverage-reports',
|
|
201
237
|
reports: [
|
|
202
|
-
//
|
|
238
|
+
// built-in reports
|
|
203
239
|
['console-summary'],
|
|
204
240
|
['v8'],
|
|
205
241
|
['html', {
|
|
@@ -468,7 +504,7 @@ export interface CoverageRange {
|
|
|
468
504
|
* @functionName can be an empty string.
|
|
469
505
|
* @ranges is always non-empty. The first range is called the "root range".
|
|
470
506
|
* @isBlockCoverage indicates if the function has block coverage information.
|
|
471
|
-
If this is false, it usually means that the
|
|
507
|
+
If this is false, it usually means that the function was never called.
|
|
472
508
|
It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
|
|
473
509
|
*/
|
|
474
510
|
export interface FunctionCoverage {
|
|
@@ -499,10 +535,25 @@ export type V8CoverageData = ScriptCoverage[];
|
|
|
499
535
|
| Firefox (2%) | ❌ | |
|
|
500
536
|
| Node.js | ✅ | |
|
|
501
537
|
| Deno | ❌ | [issue](https://github.com/denoland/deno/issues/23359) |
|
|
502
|
-
| Bun | ❌ |
|
|
538
|
+
| Bun | ❌ | [issue](https://github.com/oven-sh/bun/issues/30968) |
|
|
539
|
+
|
|
540
|
+
> MCR 为每个 V8 覆盖率条目扩展了以下字段:
|
|
541
|
+
|
|
542
|
+
| 字段 | 类型 | 说明 |
|
|
543
|
+
| :-- | :-- | :-- |
|
|
544
|
+
| `url` | `string` | 脚本 URL 或文件路径 |
|
|
545
|
+
| `type` | `"js" \| "css"` | 条目类型 |
|
|
546
|
+
| `source` | `string` | (JS)源代码文本 |
|
|
547
|
+
| `sourceMap` | `object` | (JS)已解析的 sourcemap 对象 |
|
|
548
|
+
| `scriptOffset` | `number` | (JS)脚本包裹层的字节偏移(例如 `vm` 模块中)。此偏移会自动加到所有 range 的 start/end 值上 |
|
|
549
|
+
| `distFile` | `string` | (JS)该 source 解包自的 dist 打包文件 |
|
|
550
|
+
| `empty` | `boolean` | 当条目通过 `all` 选项添加(无真实覆盖率数据)时标记为 `true`,显示为 0% 覆盖 |
|
|
551
|
+
| `fake` | `boolean` | 当源代码非原始代码(例如从 `.ts`/`.jsx` 编译而来)时标记为 `true`。AST 解析器会跳过 fake 源的覆盖率提取 |
|
|
552
|
+
|
|
553
|
+
这些字段可以在 [`onEntry`](#hooks) 钩子中读取或修改。
|
|
503
554
|
|
|
504
555
|
## Filtering Results
|
|
505
|
-
|
|
556
|
+
### Using `entryFilter` and `sourceFilter` to filter the results for V8 report
|
|
506
557
|
当收集到V8的覆盖数据时,它实际上包含了所有的入口文件的覆盖率数据, 比如有以下3个文件:
|
|
507
558
|
|
|
508
559
|
- *dist/main.js*
|
|
@@ -576,7 +627,7 @@ const coverageOptions = {
|
|
|
576
627
|
filter: {
|
|
577
628
|
'**/node_modules/**': false,
|
|
578
629
|
'**/vendor.js': false,
|
|
579
|
-
'**/src/**': true
|
|
630
|
+
'**/src/**': true,
|
|
580
631
|
'**/**': true
|
|
581
632
|
}
|
|
582
633
|
};
|
|
@@ -675,8 +726,10 @@ const coverageOptions = {
|
|
|
675
726
|
};
|
|
676
727
|
```
|
|
677
728
|
|
|
678
|
-
##
|
|
679
|
-
|
|
729
|
+
## Hooks
|
|
730
|
+
|
|
731
|
+
### `onEnd` — 报告生成后触发
|
|
732
|
+
典型用法:检测覆盖率是否达标,低于 threshold 时抛错退出。
|
|
680
733
|
```js
|
|
681
734
|
const EC = require('eight-colors');
|
|
682
735
|
const coverageOptions = {
|
|
@@ -706,6 +759,29 @@ const coverageOptions = {
|
|
|
706
759
|
}
|
|
707
760
|
```
|
|
708
761
|
|
|
762
|
+
### `onEntry` — 每个V8入口在处理前触发(仅V8)
|
|
763
|
+
可以在进入AST解析前改写 entry(例如对非标准语法的源码做预编译),参见 [Unparsable source](#unparsable-source)
|
|
764
|
+
```js
|
|
765
|
+
const coverageOptions = {
|
|
766
|
+
onEntry: async (entry) => {
|
|
767
|
+
// entry.source, entry.sourceMap, entry.fake, ...
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### `onStart` / `onReady` — 仅CLI可用
|
|
773
|
+
只在使用 `mcr <command>` 时有效:
|
|
774
|
+
- `onStart(coverageReport)` — 子进程启动前触发
|
|
775
|
+
- `onReady(coverageReport, nodeV8CoverageDir, subprocess)` — 子进程退出后、MCR 读取 `nodeV8CoverageDir` 之前触发。用于子进程尚未写完覆盖数据需要等待的场景
|
|
776
|
+
```js
|
|
777
|
+
// mcr.config.js
|
|
778
|
+
module.exports = {
|
|
779
|
+
onReady: async (coverageReport, nodeV8CoverageDir, subprocess) => {
|
|
780
|
+
// 等待某个信号,或先对 raw 文件做一些预处理
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
```
|
|
784
|
+
|
|
709
785
|
## Ignoring Uncovered Codes
|
|
710
786
|
使用特定的注释,以`v8 ignore `开头可以忽略未覆盖的代码:
|
|
711
787
|
- 忽略开始到结束
|
|
@@ -718,9 +794,9 @@ function uncovered() {
|
|
|
718
794
|
- 忽略接下来一行或者多行
|
|
719
795
|
```js
|
|
720
796
|
/* v8 ignore next */
|
|
721
|
-
const os = platform === '
|
|
797
|
+
const os = platform === 'win32' ? 'Windows' : 'Other';
|
|
722
798
|
|
|
723
|
-
const os = platform === '
|
|
799
|
+
const os = platform === 'win32' ? 'Windows' /* v8 ignore next */ : 'Other';
|
|
724
800
|
|
|
725
801
|
// v8 ignore next 3
|
|
726
802
|
if (platform === 'linux') {
|
|
@@ -739,6 +815,7 @@ function uncovered() {
|
|
|
739
815
|
}
|
|
740
816
|
/* node:coverage enable */
|
|
741
817
|
```
|
|
818
|
+
- 可以设置 `v8Ignore: false` 完全禁用基于注释的忽略机制。
|
|
742
819
|
|
|
743
820
|
## Multiprocessing Support
|
|
744
821
|
> 多进程支持可以很好的解决异步并行的情况。所有的覆盖率数据会保存到`[outputDir]/.cache`,在报告生成之后,这些缓存数据会被清除。除非开启了[调试模式](#debug-for-coverage-and-sourcemap),或者使用了`raw`报告
|
|
@@ -792,14 +869,42 @@ npm i monocart-coverage-reports
|
|
|
792
869
|
npx mcr node ./test/specs/node.test.js -r v8,console-details --lcov
|
|
793
870
|
```
|
|
794
871
|
|
|
795
|
-
-
|
|
796
|
-
|
|
872
|
+
- CLI 选项
|
|
873
|
+
|
|
874
|
+
以下选项仅在 CLI 模式(`mcr <command>`)下可用:
|
|
875
|
+
|
|
876
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
877
|
+
| :-- | :-- | :-- | :-- |
|
|
878
|
+
| `-c, --config` | `string` | — | 自定义配置文件路径 |
|
|
879
|
+
| `--import` | `string` | `null` | 通过 `NODE_OPTIONS` 预加载 ESM 模块,如 `tsx`。需要 Node.js `>= 18.19` |
|
|
880
|
+
| `--require` | `string` | `null` | 通过 `NODE_OPTIONS` 预加载 CJS 模块 |
|
|
881
|
+
| `--env` | `string` | `null` | 从 dotenv 文件加载环境变量(默认 `.env`)。需要 Node.js `>= 20.6` |
|
|
882
|
+
| `onStart` | `(coverageReport) => Promise<void>` | `null` | 子进程启动前触发;参见 [Hooks](#hooks) |
|
|
883
|
+
| `onReady` | `(coverageReport, nodeV8CoverageDir, subprocess) => Promise<void>` | `null` | 子进程退出后、MCR 读取覆盖率前触发;参见 [Hooks](#hooks) |
|
|
884
|
+
|
|
885
|
+
其他选项(如 `--name`、`--reports`、`--outputDir`、`--lcov` 等)参见 [选项配置](#options) 表格,它们在 CLI 中的用法相同。
|
|
797
886
|
|
|
798
887
|
- 使用 `--` 可以隔离子程序参数,以免两种参数混淆
|
|
799
888
|
```sh
|
|
800
889
|
mcr -c mcr.config.js -- sub-cli -c sub-cli.config.js
|
|
801
890
|
```
|
|
802
891
|
|
|
892
|
+
- `--import <module>` / `--require <module>`: 通过 `NODE_OPTIONS` 把一个预加载模块传给子进程。常用于 TypeScript/JSX 运行时(`tsx`、`ts-node` 等),也可用于加载 `mcr.config.ts`。要求 Node.js `>= 18.19`
|
|
893
|
+
```sh
|
|
894
|
+
mcr --import tsx node ./test.ts
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
- `--env [path]`: 子进程启动前从 dotenv 文件加载环境变量(默认 `.env`)。依赖 `process.loadEnvFile`,Node.js `20.6` 起支持
|
|
898
|
+
```sh
|
|
899
|
+
mcr --env .env.test node ./test.js
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
- `merge`: 直接合并 `inputDir` 中的原始覆盖率数据并生成合并报告,无需启动子进程。
|
|
903
|
+
```sh
|
|
904
|
+
mcr merge -r v8,console-details --inputDir ./coverage-reports/unit/raw,./coverage-reports/e2e/raw --outputDir ./coverage-reports/merged
|
|
905
|
+
```
|
|
906
|
+
参见 [合并覆盖率报告](#merge-coverage-reports)。
|
|
907
|
+
|
|
803
908
|
- 参见例子
|
|
804
909
|
- [Mocha](#mocha)
|
|
805
910
|
- [TypeScript](#typescript)
|
|
@@ -814,7 +919,12 @@ mcr -c mcr.config.js -- sub-cli -c sub-cli.config.js
|
|
|
814
919
|
- `mcr.config.cjs`
|
|
815
920
|
- `mcr.config.mjs`
|
|
816
921
|
- `mcr.config.json` - json format
|
|
817
|
-
- `mcr.config.ts`
|
|
922
|
+
- `mcr.config.ts` (需要预加载 TypeScript 执行模块,比如 `tsx`):
|
|
923
|
+
```sh
|
|
924
|
+
mcr --import tsx -c mcr.config.ts node ./test.js
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
> 未指定自定义路径时,MCR 会从当前工作目录向上查找默认配置文件(通过 [`find-up`](https://github.com/sindresorhus/find-up))。不在搜索路径内的文件会被忽略
|
|
818
928
|
|
|
819
929
|
## Merge Coverage Reports
|
|
820
930
|
以下这些使用场景可能需要使用合并覆盖率报告:
|
|
@@ -892,7 +1002,7 @@ const coverageOptions = {
|
|
|
892
1002
|
],
|
|
893
1003
|
|
|
894
1004
|
onEnd: () => {
|
|
895
|
-
// remove the raw files if
|
|
1005
|
+
// remove the raw files if no longer needed
|
|
896
1006
|
// inputDir.forEach((p) => {
|
|
897
1007
|
// fs.rmSync(p, {
|
|
898
1008
|
// recursive: true,
|
package/lib/assets.js
CHANGED
package/lib/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const { pathToFileURL } = require('url');
|
|
4
5
|
const EC = require('eight-colors');
|
|
5
6
|
|
|
6
7
|
const { program } = require('commander');
|
|
@@ -11,12 +12,18 @@ const Util = require('./utils/util.js');
|
|
|
11
12
|
|
|
12
13
|
const version = require('../package.json').version;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
// quote NODE_OPTIONS argument if it contains whitespace so child processes
|
|
16
|
+
// inheriting the env var can parse it correctly
|
|
17
|
+
const quoteNodeOption = (v) => ((/\s/).test(v) ? `"${v}"` : v);
|
|
18
|
+
|
|
19
|
+
// absolute path so NODE_OPTIONS keeps resolving when child processes are
|
|
20
|
+
// spawned with a different cwd than the mcr parent process
|
|
21
|
+
const getRequirePath = () => {
|
|
22
|
+
return quoteNodeOption(path.resolve(__dirname, 'register', 'register.js'));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getImportPath = () => {
|
|
26
|
+
return quoteNodeOption(pathToFileURL(path.resolve(__dirname, 'register', 'register.mjs')).href);
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
const getInitNodeOptions = async (cliOptions) => {
|
|
@@ -92,10 +99,10 @@ const initNodeOptions = async (cliOptions) => {
|
|
|
92
99
|
|
|
93
100
|
// export source after
|
|
94
101
|
if (preloadType === '--import') {
|
|
95
|
-
const importPath =
|
|
102
|
+
const importPath = getImportPath();
|
|
96
103
|
nodeOptions.unshift(`--import ${importPath}`);
|
|
97
104
|
} else {
|
|
98
|
-
const requirePath =
|
|
105
|
+
const requirePath = getRequirePath();
|
|
99
106
|
nodeOptions.unshift(`--require ${requirePath}`);
|
|
100
107
|
}
|
|
101
108
|
|
|
@@ -183,6 +190,7 @@ const executeCommand = async (command, cliOptions) => {
|
|
|
183
190
|
process.on('uncaughtException', function(err) {
|
|
184
191
|
Util.logError(`Process uncaughtException: ${err.message}`);
|
|
185
192
|
console.log(err.stack);
|
|
193
|
+
process.exit(1);
|
|
186
194
|
});
|
|
187
195
|
|
|
188
196
|
// the -- separator
|