monocart-coverage-reports 2.5.7 → 2.5.9
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 +103 -61
- package/lib/converter/ast.js +18 -3
- package/lib/converter/converter.js +52 -6
- package/lib/default/options.js +11 -1
- package/lib/generate.js +200 -1
- package/lib/index.d.ts +34 -5
- package/lib/index.js +16 -6
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/utils/source-path.js +16 -2
- package/lib/utils/util.js +2 -10
- package/lib/v8/v8-summary.js +8 -0
- package/lib/v8/v8.js +23 -5
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
- [Playwright](#playwright)
|
|
38
38
|
- [Jest](#jest)
|
|
39
39
|
- [Vitest](#vitest)
|
|
40
|
+
- [CodeceptJS](#codeceptjs)
|
|
41
|
+
- [WebdriverIO](#webdriverio)
|
|
40
42
|
- [Codecov](#codecov)
|
|
41
43
|
- [Coveralls](#coveralls)
|
|
42
44
|
- [Sonar Cloud](#sonar-cloud)
|
|
@@ -46,15 +48,22 @@
|
|
|
46
48
|
## Usage
|
|
47
49
|
```js
|
|
48
50
|
const MCR = require('monocart-coverage-reports');
|
|
49
|
-
const
|
|
51
|
+
const coverageOptions = {
|
|
52
|
+
name: 'My Coverage Report - 2024-02-28',
|
|
50
53
|
outputDir: './coverage-reports',
|
|
51
|
-
reports: "v8"
|
|
54
|
+
reports: ["v8", "console-details"]
|
|
52
55
|
}
|
|
53
|
-
const coverageReport = MCR(
|
|
56
|
+
const coverageReport = MCR(coverageOptions);
|
|
57
|
+
coverageReport.cleanCache();
|
|
58
|
+
|
|
54
59
|
await coverageReport.add(coverageData1);
|
|
55
60
|
await coverageReport.add(coverageData2);
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
|
|
62
|
+
await coverageReport.generate();
|
|
63
|
+
|
|
64
|
+
// Or
|
|
65
|
+
// const { CoverageReport } = require('monocart-coverage-reports');
|
|
66
|
+
// const coverageReport = new CoverageReport(coverageOptions);
|
|
58
67
|
```
|
|
59
68
|
- [example v8](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js)
|
|
60
69
|
- [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
|
|
@@ -132,7 +141,7 @@ console.log(coverageResults.summary);
|
|
|
132
141
|
### Multiple Reports:
|
|
133
142
|
```js
|
|
134
143
|
const MCR = require('monocart-coverage-reports');
|
|
135
|
-
const
|
|
144
|
+
const coverageOptions = {
|
|
136
145
|
outputDir: './coverage-reports',
|
|
137
146
|
reports: [
|
|
138
147
|
// build-in reports
|
|
@@ -151,14 +160,15 @@ const options = {
|
|
|
151
160
|
["custom-reporter-1"],
|
|
152
161
|
["custom-reporter-2", {
|
|
153
162
|
type: "istanbul",
|
|
154
|
-
|
|
163
|
+
key: "value"
|
|
155
164
|
}],
|
|
156
165
|
// Specify reporter name with local path
|
|
157
166
|
['/absolute/path/to/custom-reporter.js']
|
|
158
167
|
|
|
159
168
|
]
|
|
160
169
|
}
|
|
161
|
-
const coverageReport = MCR(
|
|
170
|
+
const coverageReport = MCR(coverageOptions);
|
|
171
|
+
coverageReport.cleanCache();
|
|
162
172
|
```
|
|
163
173
|
|
|
164
174
|
## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
|
|
@@ -190,12 +200,22 @@ const coverageOptions = {
|
|
|
190
200
|
sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
|
|
191
201
|
};
|
|
192
202
|
```
|
|
193
|
-
Or `minimatch` pattern:
|
|
203
|
+
Or using `minimatch` pattern:
|
|
194
204
|
```js
|
|
195
205
|
const coverageOptions = {
|
|
196
206
|
entryFilter: "**/main.js",
|
|
197
207
|
sourceFilter: "**/src/**"
|
|
198
208
|
};
|
|
209
|
+
// supports multiple patterns:
|
|
210
|
+
const coverageOptions = {
|
|
211
|
+
entryFilter: {
|
|
212
|
+
'**/vendor.js': false,
|
|
213
|
+
'**/main.js': true
|
|
214
|
+
},
|
|
215
|
+
sourceFilter: {
|
|
216
|
+
'**/src/**': true
|
|
217
|
+
}
|
|
218
|
+
};
|
|
199
219
|
```
|
|
200
220
|
|
|
201
221
|
## onEnd Hook
|
|
@@ -343,21 +363,31 @@ await MCR(coverageOptions).add(jsCoverage);
|
|
|
343
363
|
## Collecting Raw V8 Coverage Data with Puppeteer
|
|
344
364
|
> Puppeteer does not provide raw v8 coverage data by default. A simple conversion is required, see example: [./test/test-puppeteer.js](./test/test-puppeteer.js)
|
|
345
365
|
```js
|
|
346
|
-
await
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
366
|
+
await Promise.all([
|
|
367
|
+
page.coverage.startJSCoverage({
|
|
368
|
+
resetOnNavigation: false,
|
|
369
|
+
// provide raw v8 coverage data
|
|
370
|
+
includeRawScriptCoverage: true
|
|
371
|
+
}),
|
|
372
|
+
page.coverage.startCSSCoverage({
|
|
373
|
+
resetOnNavigation: false
|
|
374
|
+
})
|
|
375
|
+
]);
|
|
350
376
|
|
|
351
377
|
await page.goto(url);
|
|
352
378
|
|
|
353
|
-
const jsCoverage = await
|
|
354
|
-
|
|
355
|
-
|
|
379
|
+
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
380
|
+
page.coverage.stopJSCoverage(),
|
|
381
|
+
page.coverage.stopCSSCoverage()
|
|
382
|
+
]);
|
|
383
|
+
|
|
384
|
+
// to raw V8 script coverage
|
|
385
|
+
const coverageData = [... jsCoverage.map((it) => {
|
|
356
386
|
return {
|
|
357
387
|
source: it.text,
|
|
358
388
|
... it.rawScriptCoverage
|
|
359
389
|
};
|
|
360
|
-
}
|
|
390
|
+
}), ... cssCoverage];
|
|
361
391
|
```
|
|
362
392
|
|
|
363
393
|
## Node.js V8 Coverage Report for Server Side
|
|
@@ -397,34 +427,40 @@ Possible solutions:
|
|
|
397
427
|
> 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)
|
|
398
428
|
- Main process, before the start of testing
|
|
399
429
|
```js
|
|
400
|
-
// clean previous cache before the start of testing (option)
|
|
401
430
|
const MCR = require('monocart-coverage-reports');
|
|
402
|
-
const
|
|
403
|
-
MCR(
|
|
431
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
432
|
+
const coverageReport = MCR(coverageOptions);
|
|
433
|
+
// clean previous cache before the start of testing
|
|
434
|
+
// unless the running environment is new and no cache
|
|
435
|
+
coverageReport.cleanCache();
|
|
404
436
|
```
|
|
405
437
|
|
|
406
438
|
- Sub process 1, testing stage 1
|
|
407
439
|
```js
|
|
408
440
|
const MCR = require('monocart-coverage-reports');
|
|
409
|
-
const
|
|
410
|
-
|
|
441
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
442
|
+
const coverageReport = MCR(coverageOptions);
|
|
443
|
+
// do not clean cache in the stage
|
|
444
|
+
await coverageReport.add(coverageData1);
|
|
411
445
|
```
|
|
412
446
|
|
|
413
447
|
- Sub process 2, testing stage 2
|
|
414
448
|
```js
|
|
415
449
|
const MCR = require('monocart-coverage-reports');
|
|
416
|
-
const
|
|
417
|
-
|
|
450
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
451
|
+
const coverageReport = MCR(coverageOptions);
|
|
452
|
+
// do not clean cache in the stage
|
|
453
|
+
await coverageReport.add(coverageData2);
|
|
418
454
|
```
|
|
419
455
|
|
|
420
456
|
- Main process, after the completion of testing
|
|
421
457
|
```js
|
|
422
458
|
// generate coverage reports after the completion of testing
|
|
423
459
|
const MCR = require('monocart-coverage-reports');
|
|
424
|
-
const
|
|
425
|
-
const coverageReport = MCR(
|
|
426
|
-
|
|
427
|
-
|
|
460
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
461
|
+
const coverageReport = MCR(coverageOptions);
|
|
462
|
+
// do not clean cache before generating reports
|
|
463
|
+
await coverageReport.generate();
|
|
428
464
|
```
|
|
429
465
|
|
|
430
466
|
## Merge Coverage Reports
|
|
@@ -451,6 +487,7 @@ const coverageOptions = {
|
|
|
451
487
|
```
|
|
452
488
|
Then, after all the tests are completed, generate a merged report with option `inputDir`:
|
|
453
489
|
```js
|
|
490
|
+
// esm syntax
|
|
454
491
|
import fs from "fs";
|
|
455
492
|
import { CoverageReport } from 'monocart-coverage-reports';
|
|
456
493
|
const coverageOptions = {
|
|
@@ -503,42 +540,41 @@ const coverageOptions = {
|
|
|
503
540
|
```
|
|
504
541
|
|
|
505
542
|
## Adding Empty Coverage for Untested Files
|
|
506
|
-
By default the untested files will not be included in the coverage report, we can
|
|
543
|
+
By default the untested files will not be included in the coverage report, we can add empty coverage data for all files with option `all`, the untested files will show 0% coverage.
|
|
507
544
|
```js
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const coverageData = [];
|
|
515
|
-
const fileList = fs.readdirSync(dir);
|
|
516
|
-
for (const filename of fileList) {
|
|
517
|
-
const filePath = path.resolve(dir, filename);
|
|
518
|
-
const source = fs.readFileSync(filePath).toString('utf-8');
|
|
519
|
-
const sourcePath = path.relative(process.cwd(), filePath);
|
|
520
|
-
const url = pathToFileURL(sourcePath).toString();
|
|
521
|
-
const extname = path.extname(filename);
|
|
522
|
-
if (['.css'].includes(extname)) {
|
|
523
|
-
coverageData.push({
|
|
524
|
-
empty: true,
|
|
525
|
-
type: 'css',
|
|
526
|
-
url,
|
|
527
|
-
text: source
|
|
528
|
-
});
|
|
529
|
-
} else {
|
|
530
|
-
coverageData.push({
|
|
531
|
-
empty: true,
|
|
532
|
-
type: 'js',
|
|
533
|
-
url,
|
|
534
|
-
source
|
|
535
|
-
});
|
|
545
|
+
const coverageOptions = {
|
|
546
|
+
all: {
|
|
547
|
+
dir: ['./src'],
|
|
548
|
+
filter: (filePath) => {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
536
551
|
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
552
|
+
};
|
|
553
|
+
```
|
|
554
|
+
The filter also supports `minimatch` pattern:
|
|
555
|
+
```js
|
|
556
|
+
const coverageOptions = {
|
|
557
|
+
all: {
|
|
558
|
+
dir: ['./src'],
|
|
559
|
+
filter: '**/*.js'
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
// or multiple patterns
|
|
563
|
+
const coverageOptions = {
|
|
564
|
+
all: {
|
|
565
|
+
dir: ['./src'],
|
|
566
|
+
filter: {
|
|
567
|
+
// exclude files
|
|
568
|
+
'**/ignored-*.js': false,
|
|
569
|
+
'**/*.html': false,
|
|
570
|
+
'**/*.ts': false,
|
|
571
|
+
// empty css coverage
|
|
572
|
+
'**/*.scss': "css",
|
|
573
|
+
'**/*': true
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
};
|
|
540
577
|
```
|
|
541
|
-
see example: [./test/cli-options.js](./test/cli-options.js)
|
|
542
578
|
|
|
543
579
|
## Ignoring Uncovered Codes
|
|
544
580
|
To ignore codes, use the special comment which starts with `v8 ignore `:
|
|
@@ -675,6 +711,12 @@ When `logging` is `debug`, the raw report data will be preserved in `[outputDir]
|
|
|
675
711
|
### [Vitest](https://github.com/vitest-dev/vitest)
|
|
676
712
|
- [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - A Vitest custom provider module for coverage reports
|
|
677
713
|
|
|
714
|
+
### [CodeceptJS](https://github.com/codeceptjs/CodeceptJS)
|
|
715
|
+
- [codeceptjs-monocart-coverage](https://github.com/cenfun/codeceptjs-monocart-coverage) - A CodeceptJS plugin for coverage reports
|
|
716
|
+
|
|
717
|
+
### [WebdriverIO](https://github.com/webdriverio/webdriverio)
|
|
718
|
+
- [wdio-monocart-service](https://github.com/cenfun/wdio-monocart-service) - A WebdriverIO service for coverage reports
|
|
719
|
+
|
|
678
720
|
### [Codecov](https://codecov.com/)
|
|
679
721
|
[](https://codecov.io/gh/cenfun/monocart-coverage-reports)
|
|
680
722
|
- Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
|
package/lib/converter/ast.js
CHANGED
|
@@ -167,19 +167,34 @@ const getJsAstInfo = (item, coverageList) => {
|
|
|
167
167
|
allowHashBang: true
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
+
let err;
|
|
170
171
|
let ast;
|
|
171
172
|
try {
|
|
172
173
|
ast = acorn.parse(source, options);
|
|
173
174
|
} catch (e) {
|
|
175
|
+
err = e;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (err) {
|
|
174
179
|
|
|
175
|
-
// empty = untested file
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
// empty = untested file
|
|
181
|
+
// could be .ts, .vue and so on, that can not be parsed normally
|
|
182
|
+
if (empty) {
|
|
183
|
+
return {
|
|
184
|
+
functions: [],
|
|
185
|
+
branches: [],
|
|
186
|
+
statements: []
|
|
187
|
+
};
|
|
178
188
|
}
|
|
189
|
+
|
|
190
|
+
// runtime code should be parsed successful
|
|
191
|
+
Util.logInfo(`failed to parse source file: ${item.sourcePath} ${err.message}`);
|
|
192
|
+
|
|
179
193
|
// https://github.com/acornjs/acorn/tree/master/acorn-loose
|
|
180
194
|
// It is recommended to always try a parse with the regular acorn parser first,
|
|
181
195
|
// and only fall back to this parser when that one finds syntax errors.
|
|
182
196
|
ast = acornLoose.parse(source, options);
|
|
197
|
+
|
|
183
198
|
}
|
|
184
199
|
|
|
185
200
|
const coverageInfo = getCoverageInfo(coverageList);
|
|
@@ -697,6 +697,9 @@ const handleOriginalBytesCoverage = (state, originalStateMap) => {
|
|
|
697
697
|
// });
|
|
698
698
|
}
|
|
699
699
|
|
|
700
|
+
|
|
701
|
+
const checkList = [];
|
|
702
|
+
|
|
700
703
|
// check original bytes if fully in a wrapper range
|
|
701
704
|
originalStateMap.forEach((originalState) => {
|
|
702
705
|
const bytes = originalState.bytes;
|
|
@@ -727,6 +730,30 @@ const handleOriginalBytesCoverage = (state, originalStateMap) => {
|
|
|
727
730
|
// console.log('===========================================================', originalState.sourcePath);
|
|
728
731
|
// console.log(originalState.source.length, endMapping);
|
|
729
732
|
|
|
733
|
+
checkList.push({
|
|
734
|
+
originalState,
|
|
735
|
+
startOffset,
|
|
736
|
+
endOffset
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
if (!checkList.length) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// there is no state.bytes if not in debug
|
|
746
|
+
// should using coverageList to generate bytes first
|
|
747
|
+
handleBytesCoverage(state);
|
|
748
|
+
|
|
749
|
+
checkList.forEach((it) => {
|
|
750
|
+
|
|
751
|
+
const {
|
|
752
|
+
originalState, startOffset, endOffset
|
|
753
|
+
} = it;
|
|
754
|
+
|
|
755
|
+
// console.log('=============', 'no bytes', originalState.sourcePath);
|
|
756
|
+
|
|
730
757
|
// only check uncovered range
|
|
731
758
|
// because a uncovered range could be in a covered wrapper
|
|
732
759
|
// { start: 0, end: 12137, count: 1 }, could be { start: > 0, end: < 12137, count: 0 }
|
|
@@ -735,7 +762,8 @@ const handleOriginalBytesCoverage = (state, originalStateMap) => {
|
|
|
735
762
|
continue;
|
|
736
763
|
}
|
|
737
764
|
if (startOffset >= range.start && endOffset <= range.end) {
|
|
738
|
-
|
|
765
|
+
// console.log('------------', 'added');
|
|
766
|
+
originalState.bytes.push({
|
|
739
767
|
start: 0,
|
|
740
768
|
end: originalState.source.length,
|
|
741
769
|
count: 0
|
|
@@ -746,6 +774,7 @@ const handleOriginalBytesCoverage = (state, originalStateMap) => {
|
|
|
746
774
|
|
|
747
775
|
});
|
|
748
776
|
|
|
777
|
+
|
|
749
778
|
};
|
|
750
779
|
|
|
751
780
|
// ========================================================================================================
|
|
@@ -841,17 +870,34 @@ const getOriginalDecodedMappings = (originalDecodedMap, sourceIndex, locator) =>
|
|
|
841
870
|
// ========================================================================================================
|
|
842
871
|
|
|
843
872
|
const getSourceFilter = (options) => {
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
873
|
+
const input = options.sourceFilter;
|
|
874
|
+
|
|
875
|
+
// for function handler
|
|
876
|
+
if (typeof input === 'function') {
|
|
877
|
+
return input;
|
|
847
878
|
}
|
|
848
879
|
|
|
849
|
-
|
|
880
|
+
// for single minimatch pattern
|
|
881
|
+
if (input && typeof input === 'string') {
|
|
850
882
|
return (sourcePath) => {
|
|
851
|
-
return minimatch(sourcePath,
|
|
883
|
+
return minimatch(sourcePath, input);
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// for patterns
|
|
888
|
+
if (input && typeof input === 'object') {
|
|
889
|
+
const patterns = Object.keys(input);
|
|
890
|
+
return (sourcePath) => {
|
|
891
|
+
for (const pattern of patterns) {
|
|
892
|
+
if (minimatch(sourcePath, pattern)) {
|
|
893
|
+
return input[pattern];
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
// false if not matched
|
|
852
897
|
};
|
|
853
898
|
}
|
|
854
899
|
|
|
900
|
+
// default
|
|
855
901
|
return () => true;
|
|
856
902
|
};
|
|
857
903
|
|
package/lib/default/options.js
CHANGED
|
@@ -46,6 +46,13 @@ module.exports = {
|
|
|
46
46
|
// sourceFilter: (sourcePath) => sourcePath.search(/src\/.+/) !== -1,
|
|
47
47
|
sourceFilter: null,
|
|
48
48
|
|
|
49
|
+
// options for adding empty coverage for all files
|
|
50
|
+
// all: {
|
|
51
|
+
// dir: ['src'],
|
|
52
|
+
// filter: (sourcePath) => true
|
|
53
|
+
// },
|
|
54
|
+
all: null,
|
|
55
|
+
|
|
49
56
|
// (V8 only) {boolean} Enable/Disable ignoring uncovered codes with the special comments: v8 ignore next/next N/start/stop
|
|
50
57
|
v8Ignore: true,
|
|
51
58
|
|
|
@@ -65,9 +72,12 @@ module.exports = {
|
|
|
65
72
|
// {object} { bytes:[50,80], statements:[50,80], branches:[50,80], functions:[50,80], lines:[50,80] }
|
|
66
73
|
watermarks: [50, 80],
|
|
67
74
|
|
|
68
|
-
// {boolean} clean previous files in output dir before generating report. Defaults to true.
|
|
75
|
+
// {boolean} Indicates whether to clean previous files in output dir before generating report. Defaults to true.
|
|
69
76
|
clean: true,
|
|
70
77
|
|
|
78
|
+
// {boolean} Indicates whether to clean previous cache in output dir before generating report. Defaults to false.
|
|
79
|
+
cleanCache: false,
|
|
80
|
+
|
|
71
81
|
// {function} onEnd hook
|
|
72
82
|
// onEnd: async (reportData) => {}
|
|
73
83
|
onEnd: null
|
package/lib/generate.js
CHANGED
|
@@ -11,6 +11,9 @@ const { mergeV8Coverage, saveV8Report } = require('./v8/v8.js');
|
|
|
11
11
|
|
|
12
12
|
const { convertV8List } = require('./converter/converter.js');
|
|
13
13
|
|
|
14
|
+
const { minimatch } = require('./packages/monocart-coverage-vendor.js');
|
|
15
|
+
const { pathToFileURL } = require('url');
|
|
16
|
+
|
|
14
17
|
// ========================================================================================================
|
|
15
18
|
|
|
16
19
|
// maybe v8 or to istanbul reports
|
|
@@ -288,8 +291,175 @@ const generateCoverageReports = async (dataList, sourceCache, options) => {
|
|
|
288
291
|
|
|
289
292
|
// ========================================================================================================
|
|
290
293
|
|
|
294
|
+
const resolveAllDirList = (input) => {
|
|
295
|
+
const dirs = Util.toList(input, ',');
|
|
296
|
+
if (!dirs.length) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const dirList = dirs.filter((it) => fs.existsSync(it));
|
|
300
|
+
if (!dirList.length) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
return dirList;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const resolveAllFilter = (input) => {
|
|
307
|
+
// for function handler
|
|
308
|
+
if (typeof input === 'function') {
|
|
309
|
+
return input;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// for single minimatch pattern
|
|
313
|
+
if (input && typeof input === 'string') {
|
|
314
|
+
return (filePath) => {
|
|
315
|
+
return minimatch(filePath, input);
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// for patterns
|
|
320
|
+
if (input && typeof input === 'object') {
|
|
321
|
+
const patterns = Object.keys(input);
|
|
322
|
+
return (filePath) => {
|
|
323
|
+
for (const pattern of patterns) {
|
|
324
|
+
if (minimatch(filePath, pattern)) {
|
|
325
|
+
return input[pattern];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// false if not matched
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// default
|
|
333
|
+
return () => true;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const resolveAllDataType = (type, dataList) => {
|
|
337
|
+
if (!type && dataList.length) {
|
|
338
|
+
const firstData = dataList[0];
|
|
339
|
+
type = firstData.type;
|
|
340
|
+
}
|
|
341
|
+
const types = {
|
|
342
|
+
v8: 'v8',
|
|
343
|
+
istanbul: 'istanbul'
|
|
344
|
+
};
|
|
345
|
+
return types[type] || types.v8;
|
|
346
|
+
};
|
|
291
347
|
|
|
292
|
-
const
|
|
348
|
+
const resolveAllOptions = (input, dataList) => {
|
|
349
|
+
if (!input) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let type;
|
|
354
|
+
let dir;
|
|
355
|
+
let filter;
|
|
356
|
+
if (typeof input === 'string' || Array.isArray(input)) {
|
|
357
|
+
dir = input;
|
|
358
|
+
filter = () => true;
|
|
359
|
+
} else {
|
|
360
|
+
type = input.type;
|
|
361
|
+
dir = input.dir;
|
|
362
|
+
filter = input.filter;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const dirList = resolveAllDirList(dir);
|
|
366
|
+
if (!dirList) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const dataType = resolveAllDataType(type, dataList);
|
|
371
|
+
const fileFilter = resolveAllFilter(filter);
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
dirList,
|
|
375
|
+
dataType,
|
|
376
|
+
fileFilter
|
|
377
|
+
};
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const resolveFileType = (fileType, filePath) => {
|
|
381
|
+
if (fileType === 'js' || fileType === 'css') {
|
|
382
|
+
return fileType;
|
|
383
|
+
}
|
|
384
|
+
const extname = path.extname(filePath);
|
|
385
|
+
if (['.css', '.scss', '.sass', '.less'].includes(extname)) {
|
|
386
|
+
return 'css';
|
|
387
|
+
}
|
|
388
|
+
return 'js';
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const getV8EmptyData = (fileList) => {
|
|
392
|
+
const v8DataList = [];
|
|
393
|
+
fileList.forEach((item) => {
|
|
394
|
+
const { filePath, fileType } = item;
|
|
395
|
+
const type = resolveFileType(fileType, filePath);
|
|
396
|
+
const url = pathToFileURL(filePath).toString();
|
|
397
|
+
const source = Util.readFileSync(filePath);
|
|
398
|
+
const entryFile = {
|
|
399
|
+
empty: true,
|
|
400
|
+
type,
|
|
401
|
+
url,
|
|
402
|
+
// for empty, css supports both source and text
|
|
403
|
+
source
|
|
404
|
+
};
|
|
405
|
+
v8DataList.push(entryFile);
|
|
406
|
+
});
|
|
407
|
+
return v8DataList;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const getIstanbulEmptyData = (fileList) => {
|
|
411
|
+
const istanbulData = {};
|
|
412
|
+
fileList.forEach((item) => {
|
|
413
|
+
const { filePath } = item;
|
|
414
|
+
istanbulData[filePath] = {
|
|
415
|
+
path: filePath,
|
|
416
|
+
statementMap: {},
|
|
417
|
+
fnMap: {},
|
|
418
|
+
branchMap: {},
|
|
419
|
+
s: {},
|
|
420
|
+
f: {},
|
|
421
|
+
b: {}
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
return istanbulData;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const getEmptyData = (all, dataList) => {
|
|
428
|
+
const allOptions = resolveAllOptions(all, dataList);
|
|
429
|
+
// console.log('allOptions', all, allOptions);
|
|
430
|
+
if (!allOptions) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const {
|
|
434
|
+
dirList, dataType, fileFilter
|
|
435
|
+
} = allOptions;
|
|
436
|
+
const fileList = [];
|
|
437
|
+
dirList.forEach((dir) => {
|
|
438
|
+
Util.forEachFile(dir, [], (fileName, fileDir) => {
|
|
439
|
+
const filePath = path.resolve(fileDir, fileName);
|
|
440
|
+
const fileType = fileFilter(filePath);
|
|
441
|
+
if (fileType) {
|
|
442
|
+
fileList.push({
|
|
443
|
+
filePath,
|
|
444
|
+
fileType
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
// console.log('fileList', fileList);
|
|
450
|
+
if (!fileList.length) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (dataType === 'v8') {
|
|
454
|
+
return getV8EmptyData(fileList);
|
|
455
|
+
}
|
|
456
|
+
return getIstanbulEmptyData(fileList);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// ========================================================================================================
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
const getInputData = async (inputList, all, cacheDir, addHandler) => {
|
|
293
463
|
const dataList = [];
|
|
294
464
|
const sourceCache = new Map();
|
|
295
465
|
|
|
@@ -321,6 +491,35 @@ const getInputData = async (inputList) => {
|
|
|
321
491
|
}
|
|
322
492
|
}
|
|
323
493
|
}
|
|
494
|
+
|
|
495
|
+
// add empty coverage for all files
|
|
496
|
+
// dataList for data type, before `add` we don't which one is tested, because using hash id
|
|
497
|
+
const emptyData = getEmptyData(all, dataList);
|
|
498
|
+
if (emptyData) {
|
|
499
|
+
const coverageResults = await addHandler(emptyData);
|
|
500
|
+
// after `add`
|
|
501
|
+
if (coverageResults) {
|
|
502
|
+
dataList.push(coverageResults);
|
|
503
|
+
if (coverageResults.type === 'v8') {
|
|
504
|
+
// sourceCache
|
|
505
|
+
coverageResults.data.forEach((entry) => {
|
|
506
|
+
const { id } = entry;
|
|
507
|
+
if (sourceCache.has(id)) {
|
|
508
|
+
// console.log('+', entry.sourcePath);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
// console.log('-', entry.sourcePath);
|
|
512
|
+
const sourcePath = Util.resolveCacheSourcePath(cacheDir, id);
|
|
513
|
+
const content = Util.readFileSync(sourcePath);
|
|
514
|
+
if (content) {
|
|
515
|
+
const json = JSON.parse(content);
|
|
516
|
+
sourceCache.set(json.id, json);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
324
523
|
return {
|
|
325
524
|
dataList,
|
|
326
525
|
sourceCache
|