monocart-coverage-reports 2.2.2 → 2.3.1
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 +148 -26
- package/lib/cli.js +0 -6
- package/lib/converter/ast.js +26 -5
- package/lib/converter/collect-source-maps.js +15 -3
- package/lib/converter/converter.js +364 -140
- package/lib/converter/find-original-range.js +7 -0
- package/lib/converter/info-branch.js +13 -5
- package/lib/default/options.js +3 -0
- package/lib/generate.js +132 -65
- package/lib/index.d.ts +13 -3
- package/lib/index.js +45 -15
- package/lib/istanbul/istanbul.js +7 -7
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +4 -3
- package/lib/platform/share.js +13 -0
- package/lib/utils/dedupe.js +21 -8
- package/lib/utils/source-path.js +5 -0
- package/lib/utils/util.js +30 -1
- package/lib/v8/v8.js +85 -86
- package/package.json +6 -4
- package/lib/utils/decode-mappings-worker.js +0 -9
- package/lib/utils/decode-mappings.js +0 -35
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
// https://github.com/demurgos/v8-coverage
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @
|
|
5
|
-
*
|
|
6
|
-
* @true means that the ranges form a tree of blocks representing how many times each statement or expression inside was executed.
|
|
7
|
-
* It detects skipped or repeated statements. The root range counts the number of function calls.
|
|
8
|
-
* @functionName can be an empty string. This is common for the FunctionCov representing the whole module.
|
|
2
|
+
* V8 Coverage Data Converter
|
|
3
|
+
* @copyright https://github.com/cenfun/monocart-coverage-reports
|
|
4
|
+
* @author cenfun@gmail.com
|
|
9
5
|
*/
|
|
10
|
-
// https://github.com/bcoe/v8-coverage
|
|
11
|
-
/**
|
|
12
|
-
* @ranges is always non-empty. The first range is called the "root range".
|
|
13
|
-
* @isBlockCoverage indicates if the function has block coverage information.
|
|
14
|
-
If this is false, it usually means that the functions was never called.
|
|
15
|
-
It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
|
|
16
|
-
* @functionName can be an empty string. This is common for the FunctionCov representing the whole module.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
// if you have a line of code that says `var x= 10; console.log(x);` that's one line and 2 statements.
|
|
20
|
-
|
|
21
|
-
const path = require('path');
|
|
22
|
-
|
|
23
6
|
const Util = require('../utils/util.js');
|
|
24
|
-
const decodeMappings = require('../utils/decode-mappings.js');
|
|
25
7
|
|
|
26
8
|
// position mapping for conversion between offset and line/column
|
|
27
9
|
const { Locator } = require('monocart-formatter');
|
|
@@ -29,8 +11,11 @@ const findOriginalRange = require('./find-original-range.js');
|
|
|
29
11
|
const { getJsAstInfo, getCssAstInfo } = require('./ast.js');
|
|
30
12
|
const { getIgnoredRanges } = require('./ignore.js');
|
|
31
13
|
|
|
32
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
sortRanges, dedupeCountRanges, mergeRangesWith
|
|
16
|
+
} = require('../utils/dedupe.js');
|
|
33
17
|
const { getSourceType, initSourceMapSourcePath } = require('../utils/source-path.js');
|
|
18
|
+
const { decode } = require('../packages/monocart-coverage-vendor.js');
|
|
34
19
|
|
|
35
20
|
const InfoLine = require('./info-line.js');
|
|
36
21
|
const InfoBranch = require('./info-branch.js');
|
|
@@ -226,13 +211,14 @@ const calculateV8Lines = (lines, blankCount, commentCount) => {
|
|
|
226
211
|
* * `f` - hit count for functions
|
|
227
212
|
* * `b` - hit count for branches
|
|
228
213
|
*/
|
|
229
|
-
const collectFileCoverage = (
|
|
214
|
+
const collectFileCoverage = (v8Data, state, options) => {
|
|
230
215
|
|
|
231
|
-
const { sourcePath } = item;
|
|
232
216
|
const {
|
|
233
217
|
bytes,
|
|
234
218
|
functions,
|
|
235
219
|
branches,
|
|
220
|
+
|
|
221
|
+
sourcePath,
|
|
236
222
|
locator
|
|
237
223
|
} = state;
|
|
238
224
|
|
|
@@ -277,11 +263,13 @@ const collectFileCoverage = (item, state, coverageData, options) => {
|
|
|
277
263
|
data.functions = functions.map((info) => {
|
|
278
264
|
return info.getRange();
|
|
279
265
|
});
|
|
266
|
+
sortRanges(data.functions);
|
|
280
267
|
|
|
281
268
|
// branch group with locations to flat branches
|
|
282
269
|
data.branches = branches.map((info) => {
|
|
283
270
|
return info.getRanges();
|
|
284
271
|
}).flat();
|
|
272
|
+
sortRanges(data.branches);
|
|
285
273
|
|
|
286
274
|
// ==========================================
|
|
287
275
|
// lines
|
|
@@ -292,8 +280,8 @@ const collectFileCoverage = (item, state, coverageData, options) => {
|
|
|
292
280
|
|
|
293
281
|
// ==========================================
|
|
294
282
|
// v8 data and summary
|
|
295
|
-
|
|
296
|
-
|
|
283
|
+
v8Data.data = data;
|
|
284
|
+
v8Data.summary = {
|
|
297
285
|
functions: calculateV8Functions(data.functions),
|
|
298
286
|
branches: calculateV8Branches(data.branches),
|
|
299
287
|
lines: calculateV8Lines(lines, blankCount, commentCount)
|
|
@@ -301,7 +289,7 @@ const collectFileCoverage = (item, state, coverageData, options) => {
|
|
|
301
289
|
|
|
302
290
|
// ==========================================
|
|
303
291
|
// istanbul
|
|
304
|
-
const
|
|
292
|
+
const istanbulData = {
|
|
305
293
|
path: sourcePath,
|
|
306
294
|
|
|
307
295
|
statementMap: {},
|
|
@@ -317,8 +305,8 @@ const collectFileCoverage = (item, state, coverageData, options) => {
|
|
|
317
305
|
data.lines = {};
|
|
318
306
|
|
|
319
307
|
lines.filter((it) => !it.ignored).forEach((line, index) => {
|
|
320
|
-
|
|
321
|
-
|
|
308
|
+
istanbulData.statementMap[`${index}`] = line.generate();
|
|
309
|
+
istanbulData.s[`${index}`] = line.count;
|
|
322
310
|
|
|
323
311
|
let count = 0;
|
|
324
312
|
if (line.covered) {
|
|
@@ -331,18 +319,18 @@ const collectFileCoverage = (item, state, coverageData, options) => {
|
|
|
331
319
|
});
|
|
332
320
|
|
|
333
321
|
functions.filter((it) => !it.ignored).forEach((fn, index) => {
|
|
334
|
-
|
|
335
|
-
|
|
322
|
+
istanbulData.fnMap[`${index}`] = fn.generate(locator);
|
|
323
|
+
istanbulData.f[`${index}`] = fn.count;
|
|
336
324
|
});
|
|
337
325
|
|
|
338
326
|
branches.filter((it) => !it.ignored).forEach((branch, index) => {
|
|
339
327
|
const { map, counts } = branch.generate(locator);
|
|
340
|
-
|
|
341
|
-
|
|
328
|
+
istanbulData.branchMap[`${index}`] = map;
|
|
329
|
+
istanbulData.b[`${index}`] = counts;
|
|
342
330
|
});
|
|
343
331
|
|
|
344
|
-
//
|
|
345
|
-
|
|
332
|
+
// istanbul data
|
|
333
|
+
return istanbulData;
|
|
346
334
|
|
|
347
335
|
};
|
|
348
336
|
|
|
@@ -430,7 +418,7 @@ const handleBytesCoverage = (state) => {
|
|
|
430
418
|
|
|
431
419
|
// ========================================================================================================
|
|
432
420
|
|
|
433
|
-
const handleOriginalFunctionsCoverage = (state,
|
|
421
|
+
const handleOriginalFunctionsCoverage = (state, originalStateMap) => {
|
|
434
422
|
|
|
435
423
|
// functions only for js
|
|
436
424
|
if (!state.js) {
|
|
@@ -456,7 +444,7 @@ const handleOriginalFunctionsCoverage = (state, originalMap) => {
|
|
|
456
444
|
endOffset: end
|
|
457
445
|
};
|
|
458
446
|
|
|
459
|
-
const result = findOriginalRange(range, state,
|
|
447
|
+
const result = findOriginalRange(range, state, originalStateMap);
|
|
460
448
|
if (result.error) {
|
|
461
449
|
return;
|
|
462
450
|
}
|
|
@@ -480,7 +468,7 @@ const handleOriginalFunctionsCoverage = (state, originalMap) => {
|
|
|
480
468
|
|
|
481
469
|
};
|
|
482
470
|
|
|
483
|
-
const handleOriginalBranchesCoverage = (state,
|
|
471
|
+
const handleOriginalBranchesCoverage = (state, originalStateMap) => {
|
|
484
472
|
|
|
485
473
|
// branches only for js
|
|
486
474
|
if (!state.js) {
|
|
@@ -501,7 +489,7 @@ const handleOriginalBranchesCoverage = (state, originalMap) => {
|
|
|
501
489
|
};
|
|
502
490
|
|
|
503
491
|
// start
|
|
504
|
-
const result = findOriginalRange(bRange, state,
|
|
492
|
+
const result = findOriginalRange(bRange, state, originalStateMap);
|
|
505
493
|
if (result.error) {
|
|
506
494
|
// not in the original files
|
|
507
495
|
return;
|
|
@@ -530,7 +518,7 @@ const handleOriginalBranchesCoverage = (state, originalMap) => {
|
|
|
530
518
|
startOffset: start,
|
|
531
519
|
endOffset: end
|
|
532
520
|
};
|
|
533
|
-
const res = findOriginalRange(lRange, state,
|
|
521
|
+
const res = findOriginalRange(lRange, state, originalStateMap);
|
|
534
522
|
if (res.error) {
|
|
535
523
|
// It should not happen unless it is minify files, the SourceMap has some order problems
|
|
536
524
|
|
|
@@ -568,7 +556,7 @@ const handleOriginalBranchesCoverage = (state, originalMap) => {
|
|
|
568
556
|
|
|
569
557
|
};
|
|
570
558
|
|
|
571
|
-
const handleOriginalBytesCoverage = (state,
|
|
559
|
+
const handleOriginalBytesCoverage = (state, originalStateMap) => {
|
|
572
560
|
|
|
573
561
|
const { js, coverageList } = state;
|
|
574
562
|
|
|
@@ -584,7 +572,7 @@ const handleOriginalBytesCoverage = (state, originalMap) => {
|
|
|
584
572
|
return;
|
|
585
573
|
}
|
|
586
574
|
|
|
587
|
-
const result = findOriginalRange(range, state,
|
|
575
|
+
const result = findOriginalRange(range, state, originalStateMap);
|
|
588
576
|
if (result.error) {
|
|
589
577
|
return;
|
|
590
578
|
}
|
|
@@ -608,17 +596,58 @@ const handleOriginalBytesCoverage = (state, originalMap) => {
|
|
|
608
596
|
// });
|
|
609
597
|
}
|
|
610
598
|
|
|
599
|
+
// check original bytes if fully in a wrapper range
|
|
600
|
+
originalStateMap.forEach((originalState) => {
|
|
601
|
+
const bytes = originalState.bytes;
|
|
602
|
+
if (Util.isList(bytes)) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const { decodedMappings } = originalState;
|
|
607
|
+
const len = decodedMappings.length;
|
|
608
|
+
if (len < 2) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
// sort by original line/column
|
|
612
|
+
const startMapping = decodedMappings[0];
|
|
613
|
+
const endMapping = decodedMappings[len - 1];
|
|
614
|
+
// console.log(startMapping, endMapping);
|
|
615
|
+
const startOffset = startMapping.generatedOffset;
|
|
616
|
+
const endOffset = endMapping.generatedEndOffset || endMapping.generatedOffset;
|
|
617
|
+
|
|
618
|
+
// console.log('===========================================================', originalState.sourcePath);
|
|
619
|
+
// console.log(originalState.source.length, endMapping);
|
|
620
|
+
|
|
621
|
+
// only check uncovered range
|
|
622
|
+
// because a uncovered range could be in a covered wrapper
|
|
623
|
+
// { start: 0, end: 12137, count: 1 }, could be { start: > 0, end: < 12137, count: 0 }
|
|
624
|
+
for (const range of state.bytes) {
|
|
625
|
+
if (range.count > 0) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (startOffset >= range.start && endOffset <= range.end) {
|
|
629
|
+
bytes.push({
|
|
630
|
+
start: 0,
|
|
631
|
+
end: originalState.source.length,
|
|
632
|
+
count: 0
|
|
633
|
+
});
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
});
|
|
639
|
+
|
|
611
640
|
};
|
|
612
641
|
|
|
613
642
|
// ========================================================================================================
|
|
614
643
|
|
|
615
|
-
const decodeSourceMappings =
|
|
644
|
+
const decodeSourceMappings = (state, originalDecodedMap) => {
|
|
616
645
|
|
|
617
646
|
const generatedLocator = state.locator;
|
|
618
647
|
|
|
619
648
|
const { sources, mappings } = state.sourceMap;
|
|
620
649
|
|
|
621
|
-
const decodedList =
|
|
650
|
+
const decodedList = decode(mappings);
|
|
622
651
|
|
|
623
652
|
sources.forEach((source, i) => {
|
|
624
653
|
originalDecodedMap.set(i, []);
|
|
@@ -679,7 +708,7 @@ const getOriginalDecodedMappings = (originalDecodedMap, sourceIndex, locator) =>
|
|
|
679
708
|
// all mappings for the original file sorted
|
|
680
709
|
const decodedMappings = originalDecodedMap.get(sourceIndex);
|
|
681
710
|
|
|
682
|
-
if (!
|
|
711
|
+
if (!decodedMappings) {
|
|
683
712
|
return [];
|
|
684
713
|
}
|
|
685
714
|
|
|
@@ -713,10 +742,8 @@ const initOriginalList = (state, originalDecodedMap, options) => {
|
|
|
713
742
|
sourceFilter = () => true;
|
|
714
743
|
}
|
|
715
744
|
|
|
716
|
-
const fileSources = state.fileSources;
|
|
717
|
-
|
|
718
745
|
// create original content mappings
|
|
719
|
-
const
|
|
746
|
+
const originalStateMap = new Map();
|
|
720
747
|
|
|
721
748
|
const { sources, sourcesContent } = state.sourceMap;
|
|
722
749
|
|
|
@@ -734,9 +761,6 @@ const initOriginalList = (state, originalDecodedMap, options) => {
|
|
|
734
761
|
return;
|
|
735
762
|
}
|
|
736
763
|
|
|
737
|
-
// keep original formatted content
|
|
738
|
-
fileSources[sourcePath] = sourceContent;
|
|
739
|
-
|
|
740
764
|
const locator = new Locator(sourceContent);
|
|
741
765
|
|
|
742
766
|
const decodedMappings = getOriginalDecodedMappings(originalDecodedMap, sourceIndex, locator);
|
|
@@ -747,11 +771,11 @@ const initOriginalList = (state, originalDecodedMap, options) => {
|
|
|
747
771
|
|
|
748
772
|
const originalState = {
|
|
749
773
|
original: true,
|
|
750
|
-
//
|
|
774
|
+
// original file is js
|
|
751
775
|
js: true,
|
|
752
776
|
type,
|
|
753
|
-
sourcePath,
|
|
754
777
|
source: sourceContent,
|
|
778
|
+
sourcePath,
|
|
755
779
|
locator,
|
|
756
780
|
decodedMappings,
|
|
757
781
|
// coverage info
|
|
@@ -761,24 +785,24 @@ const initOriginalList = (state, originalDecodedMap, options) => {
|
|
|
761
785
|
astInfo: {
|
|
762
786
|
functions: [],
|
|
763
787
|
branches: []
|
|
764
|
-
}
|
|
788
|
+
},
|
|
789
|
+
// coverage data
|
|
790
|
+
v8Data: {}
|
|
765
791
|
};
|
|
766
792
|
|
|
767
|
-
|
|
793
|
+
originalStateMap.set(sourceIndex, originalState);
|
|
768
794
|
});
|
|
769
795
|
|
|
770
|
-
return
|
|
796
|
+
return originalStateMap;
|
|
771
797
|
};
|
|
772
798
|
|
|
773
|
-
const collectOriginalList = (
|
|
799
|
+
const collectOriginalList = (state, originalStateMap) => {
|
|
774
800
|
|
|
775
|
-
const { fileUrls
|
|
776
|
-
const distFile =
|
|
801
|
+
const { fileUrls } = state;
|
|
802
|
+
const distFile = state.sourcePath;
|
|
777
803
|
|
|
778
804
|
// collect original files
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
originalMap.forEach((originalState) => {
|
|
805
|
+
originalStateMap.forEach((originalState) => {
|
|
782
806
|
|
|
783
807
|
const {
|
|
784
808
|
js, type, sourcePath, source
|
|
@@ -788,7 +812,7 @@ const collectOriginalList = (item, state, originalMap, options) => {
|
|
|
788
812
|
const url = fileUrls[sourcePath] || sourcePath;
|
|
789
813
|
|
|
790
814
|
// add dist for id
|
|
791
|
-
const id = Util.calculateSha1(
|
|
815
|
+
const id = Util.calculateSha1(sourcePath + source);
|
|
792
816
|
|
|
793
817
|
const sourceItem = {
|
|
794
818
|
url,
|
|
@@ -800,31 +824,26 @@ const collectOriginalList = (item, state, originalMap, options) => {
|
|
|
800
824
|
source
|
|
801
825
|
};
|
|
802
826
|
|
|
803
|
-
//
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
sourceList.push(sourceItem);
|
|
827
|
+
// save v8 data and add to originalList
|
|
828
|
+
originalState.v8Data = sourceItem;
|
|
829
|
+
state.originalList.push(originalState);
|
|
807
830
|
});
|
|
808
831
|
|
|
809
|
-
return sourceList;
|
|
810
832
|
};
|
|
811
833
|
|
|
812
834
|
// ========================================================================================================
|
|
813
835
|
|
|
814
|
-
const generateCoverageForDist = (
|
|
836
|
+
const generateCoverageForDist = (state) => {
|
|
815
837
|
|
|
816
838
|
handleFunctionsCoverage(state);
|
|
817
839
|
handleBranchesCoverage(state);
|
|
818
840
|
handleBytesCoverage(state);
|
|
819
841
|
|
|
820
|
-
collectFileCoverage(item, state, state.coverageData, options);
|
|
821
|
-
|
|
822
842
|
};
|
|
823
843
|
|
|
824
|
-
const unpackSourceMap =
|
|
844
|
+
const unpackSourceMap = (state, options) => {
|
|
825
845
|
|
|
826
|
-
const {
|
|
827
|
-
const sourceMap = state.sourceMap;
|
|
846
|
+
const { sourceMap } = state;
|
|
828
847
|
|
|
829
848
|
// keep original urls
|
|
830
849
|
const fileUrls = {};
|
|
@@ -834,26 +853,24 @@ const unpackSourceMap = async (item, state, options) => {
|
|
|
834
853
|
|
|
835
854
|
// ===============================================
|
|
836
855
|
// decode mappings for each original file
|
|
837
|
-
|
|
856
|
+
|
|
838
857
|
const originalDecodedMap = new Map();
|
|
839
858
|
// for find-original-range
|
|
840
|
-
state.decodedMappings =
|
|
841
|
-
// only debug level
|
|
842
|
-
Util.logTime(`decode source mappings: ${sourcePath}`, time_start_decode);
|
|
859
|
+
state.decodedMappings = decodeSourceMappings(state, originalDecodedMap);
|
|
843
860
|
|
|
844
861
|
// filter original list and init list
|
|
845
|
-
const
|
|
862
|
+
const originalStateMap = initOriginalList(state, originalDecodedMap, options);
|
|
846
863
|
|
|
847
864
|
originalDecodedMap.clear();
|
|
848
865
|
|
|
849
866
|
// ===============================================
|
|
850
867
|
|
|
851
868
|
// handle functions before handle original state functions
|
|
852
|
-
handleOriginalFunctionsCoverage(state,
|
|
853
|
-
handleOriginalBranchesCoverage(state,
|
|
869
|
+
handleOriginalFunctionsCoverage(state, originalStateMap);
|
|
870
|
+
handleOriginalBranchesCoverage(state, originalStateMap);
|
|
854
871
|
|
|
855
872
|
// handle lines info before handle ranges to update line count
|
|
856
|
-
|
|
873
|
+
originalStateMap.forEach((originalState) => {
|
|
857
874
|
|
|
858
875
|
handleFunctionsCoverage(originalState);
|
|
859
876
|
handleBranchesCoverage(originalState);
|
|
@@ -865,31 +882,31 @@ const unpackSourceMap = async (item, state, options) => {
|
|
|
865
882
|
});
|
|
866
883
|
|
|
867
884
|
// handle ranges after lines ready
|
|
868
|
-
handleOriginalBytesCoverage(state,
|
|
885
|
+
handleOriginalBytesCoverage(state, originalStateMap);
|
|
869
886
|
|
|
870
887
|
// collect coverage for original list
|
|
871
|
-
|
|
888
|
+
collectOriginalList(state, originalStateMap);
|
|
872
889
|
|
|
873
890
|
};
|
|
874
891
|
|
|
875
|
-
const unpackDistFile =
|
|
892
|
+
const unpackDistFile = (item, state, options) => {
|
|
876
893
|
|
|
877
894
|
if (state.sourceMap) {
|
|
878
895
|
if (Util.loggingType === 'debug') {
|
|
879
896
|
// js self
|
|
880
897
|
item.debug = true;
|
|
881
|
-
generateCoverageForDist(
|
|
898
|
+
generateCoverageForDist(state);
|
|
882
899
|
} else {
|
|
883
900
|
item.dedupe = true;
|
|
884
901
|
}
|
|
885
902
|
|
|
886
903
|
// unpack source map
|
|
887
|
-
|
|
904
|
+
unpackSourceMap(state, options);
|
|
888
905
|
|
|
889
906
|
} else {
|
|
890
907
|
|
|
891
908
|
// css/js self
|
|
892
|
-
generateCoverageForDist(
|
|
909
|
+
generateCoverageForDist(state);
|
|
893
910
|
|
|
894
911
|
}
|
|
895
912
|
|
|
@@ -897,27 +914,260 @@ const unpackDistFile = async (item, state, options) => {
|
|
|
897
914
|
|
|
898
915
|
// ========================================================================================================
|
|
899
916
|
|
|
900
|
-
const
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
917
|
+
const filterCoverageList = (item) => {
|
|
918
|
+
const {
|
|
919
|
+
functions, scriptOffset, source
|
|
920
|
+
} = item;
|
|
921
|
+
|
|
922
|
+
// no script offset
|
|
923
|
+
if (!scriptOffset) {
|
|
924
|
+
return functions;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// vm script offset
|
|
928
|
+
const minOffset = scriptOffset;
|
|
929
|
+
// the inline sourcemap could be removed
|
|
930
|
+
const maxOffset = source.length;
|
|
931
|
+
|
|
932
|
+
const rootFunctionInfo = {
|
|
933
|
+
root: true,
|
|
934
|
+
ranges: [{
|
|
935
|
+
startOffset: minOffset,
|
|
936
|
+
endOffset: maxOffset,
|
|
937
|
+
count: 1
|
|
938
|
+
}]
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
const coverageList = functions.filter((block) => {
|
|
942
|
+
const { ranges } = block;
|
|
943
|
+
|
|
944
|
+
// first one is function coverage info
|
|
945
|
+
const functionRange = ranges[0];
|
|
946
|
+
const { startOffset, endOffset } = functionRange;
|
|
947
|
+
if (startOffset >= minOffset && endOffset <= maxOffset) {
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// blocks
|
|
952
|
+
const len = ranges.length;
|
|
953
|
+
if (len > 1) {
|
|
954
|
+
for (let i = 1; i < len; i++) {
|
|
955
|
+
const range = ranges[i];
|
|
956
|
+
if (range.startOffset >= minOffset && range.endOffset <= maxOffset) {
|
|
957
|
+
rootFunctionInfo.ranges.push(range);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
905
960
|
}
|
|
961
|
+
|
|
962
|
+
return false;
|
|
906
963
|
});
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
964
|
+
|
|
965
|
+
// first one for root function
|
|
966
|
+
if (rootFunctionInfo.ranges.length > 1) {
|
|
967
|
+
coverageList.unshift(rootFunctionInfo);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
return coverageList;
|
|
971
|
+
};
|
|
972
|
+
|
|
973
|
+
// ========================================================================================================
|
|
974
|
+
|
|
975
|
+
const isCoveredInRanges = (range, uncoveredBytes) => {
|
|
976
|
+
if (!uncoveredBytes.length) {
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
for (const item of uncoveredBytes) {
|
|
980
|
+
if (range.start >= item.start && range.end <= item.end) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
912
983
|
}
|
|
984
|
+
return true;
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
const mergeV8Data = (state, stateList) => {
|
|
988
|
+
// console.log(stateList);
|
|
989
|
+
|
|
990
|
+
// const sourcePath = state.sourcePath;
|
|
991
|
+
// console.log(sourcePath);
|
|
992
|
+
// if (sourcePath.endsWith('scroll_zoom.ts')) {
|
|
993
|
+
// console.log('merge v8 data ===================================', sourcePath);
|
|
994
|
+
// console.log(stateList.map((it) => it.bytes));
|
|
995
|
+
// console.log(stateList.map((it) => it.functions));
|
|
996
|
+
// console.log(stateList.map((it) => it.branches.map((b) => [`${b.start}-${b.end}`, JSON.stringify(b.locations.map((l) => l.count))])));
|
|
997
|
+
// }
|
|
998
|
+
|
|
999
|
+
// ===========================================================
|
|
1000
|
+
// bytes
|
|
1001
|
+
const mergedBytes = [];
|
|
1002
|
+
const uncoveredList = [];
|
|
1003
|
+
stateList.forEach((st) => {
|
|
1004
|
+
const bytes = dedupeCountRanges(st.bytes);
|
|
1005
|
+
const uncoveredBytes = [];
|
|
1006
|
+
bytes.forEach((range) => {
|
|
1007
|
+
if (range.count) {
|
|
1008
|
+
mergedBytes.push(range);
|
|
1009
|
+
} else {
|
|
1010
|
+
uncoveredBytes.push(range);
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
uncoveredList.push(uncoveredBytes);
|
|
1014
|
+
});
|
|
1015
|
+
// just remove uncovered range
|
|
1016
|
+
uncoveredList.forEach((currentBytes) => {
|
|
1017
|
+
currentBytes.forEach((range) => {
|
|
1018
|
+
for (const targetBytes of uncoveredList) {
|
|
1019
|
+
if (targetBytes === currentBytes) {
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
if (isCoveredInRanges(range, targetBytes)) {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
mergedBytes.push(range);
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
// will be dedupeCountRanges in collectFileCoverage
|
|
1031
|
+
state.bytes = mergedBytes;
|
|
1032
|
+
|
|
1033
|
+
// ===========================================================
|
|
1034
|
+
// functions
|
|
1035
|
+
const allFunctions = stateList.map((it) => it.functions).flat();
|
|
1036
|
+
const functionComparer = (lastRange, range) => {
|
|
1037
|
+
// if (lastRange.start === range.start && lastRange.end === range.end) {
|
|
1038
|
+
// return true;
|
|
1039
|
+
// }
|
|
1040
|
+
|
|
1041
|
+
// function range could be from sourcemap, not exact matched
|
|
1042
|
+
|
|
1043
|
+
// end is same
|
|
1044
|
+
// {start: 2017, end: 2315, count: 481}
|
|
1045
|
+
// {start: 2018, end: 2315, count: 14}
|
|
1046
|
+
|
|
1047
|
+
// start is same
|
|
1048
|
+
// {start: 10204, end: 10379, count: 0}
|
|
1049
|
+
// {start: 10204, end: 10393, count: 5}
|
|
1050
|
+
|
|
1051
|
+
// only one position matched could be same
|
|
1052
|
+
if (lastRange.start === range.start || lastRange.end === range.end) {
|
|
1053
|
+
// console.log(lastRange.start, range.start, lastRange.end, range.end);
|
|
1054
|
+
|
|
1055
|
+
// if (lastRange.start === range.start) {
|
|
1056
|
+
// console.log(range.end - lastRange.end, lastRange.start, lastRange.end, 'end', range.end, state.sourcePath);
|
|
1057
|
+
// } else {
|
|
1058
|
+
// console.log(range.start - lastRange.start, lastRange.start, lastRange.end, 'start', range.start, state.sourcePath);
|
|
1059
|
+
// }
|
|
1060
|
+
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
return false;
|
|
1065
|
+
};
|
|
1066
|
+
const functionHandler = (lastRange, range) => {
|
|
1067
|
+
lastRange.count += range.count;
|
|
1068
|
+
};
|
|
1069
|
+
const mergedFunctions = mergeRangesWith(allFunctions, functionComparer, functionHandler);
|
|
1070
|
+
state.functions = mergedFunctions;
|
|
1071
|
+
|
|
1072
|
+
// ===========================================================
|
|
1073
|
+
// branches
|
|
1074
|
+
const allBranches = stateList.map((it) => it.branches).flat();
|
|
1075
|
+
const branchComparer = (lastRange, range) => {
|
|
1076
|
+
// exact matched because the branch range is generated from ast
|
|
1077
|
+
return lastRange.start === range.start && lastRange.end === range.end;
|
|
1078
|
+
};
|
|
1079
|
+
const branchHandler = (lastRange, range) => {
|
|
1080
|
+
// merge locations count
|
|
1081
|
+
lastRange.locations.forEach((item, i) => {
|
|
1082
|
+
const loc = range.locations[i];
|
|
1083
|
+
if (loc) {
|
|
1084
|
+
item.count += loc.count;
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
};
|
|
1088
|
+
const mergedBranches = mergeRangesWith(allBranches, branchComparer, branchHandler);
|
|
1089
|
+
state.branches = mergedBranches;
|
|
1090
|
+
|
|
1091
|
+
// if (sourcePath.endsWith('scroll_zoom.ts')) {
|
|
1092
|
+
// console.log(mergedBytes);
|
|
1093
|
+
// console.log(mergedFunctions);
|
|
1094
|
+
// console.log(mergedBranches.map((b) => [`${b.start}-${b.end}`, JSON.stringify(b.locations.map((l) => l.count))]));
|
|
1095
|
+
// }
|
|
1096
|
+
|
|
1097
|
+
|
|
913
1098
|
};
|
|
914
1099
|
|
|
915
|
-
const
|
|
1100
|
+
const generateV8DataList = (stateList, options) => {
|
|
1101
|
+
|
|
1102
|
+
const stateMap = new Map();
|
|
1103
|
+
|
|
1104
|
+
// all original files from dist
|
|
1105
|
+
const allOriginalList = [];
|
|
1106
|
+
stateList.forEach((state) => {
|
|
1107
|
+
const { v8Data, originalList } = state;
|
|
1108
|
+
// dedupe dist file if not debug
|
|
1109
|
+
if (!v8Data.dedupe) {
|
|
1110
|
+
stateMap.set(v8Data.id, state);
|
|
1111
|
+
}
|
|
1112
|
+
allOriginalList.push(originalList);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
// merge istanbul and v8(converted)
|
|
1116
|
+
const mergeMap = new Map();
|
|
1117
|
+
allOriginalList.flat().forEach((originalState) => {
|
|
1118
|
+
const { v8Data } = originalState;
|
|
1119
|
+
const id = v8Data.id;
|
|
1120
|
+
// exists item
|
|
1121
|
+
const prevState = stateMap.get(id);
|
|
1122
|
+
if (prevState) {
|
|
1123
|
+
// ignore empty item, just override it
|
|
1124
|
+
if (!prevState.v8Data.empty) {
|
|
1125
|
+
if (mergeMap.has(id)) {
|
|
1126
|
+
mergeMap.get(id).push(originalState);
|
|
1127
|
+
} else {
|
|
1128
|
+
mergeMap.set(id, [prevState, originalState]);
|
|
1129
|
+
}
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
stateMap.set(id, originalState);
|
|
1134
|
+
});
|
|
916
1135
|
|
|
917
|
-
|
|
1136
|
+
const mergeIds = mergeMap.keys();
|
|
1137
|
+
for (const id of mergeIds) {
|
|
1138
|
+
const state = stateMap.get(id);
|
|
1139
|
+
// for source the type could be ts, so just use js (boolean)
|
|
1140
|
+
if (state.js) {
|
|
1141
|
+
mergeV8Data(state, mergeMap.get(id));
|
|
1142
|
+
} else {
|
|
1143
|
+
// should no css here, css can not be in sources
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// new v8 data list (includes sources)
|
|
1148
|
+
const v8DataList = [];
|
|
1149
|
+
// global file sources and istanbul coverage data
|
|
918
1150
|
const fileSources = {};
|
|
919
1151
|
const coverageData = {};
|
|
920
|
-
|
|
1152
|
+
stateMap.forEach((state) => {
|
|
1153
|
+
const { v8Data } = state;
|
|
1154
|
+
const istanbulData = collectFileCoverage(v8Data, state, options);
|
|
1155
|
+
const { sourcePath, source } = v8Data;
|
|
1156
|
+
v8DataList.push(v8Data);
|
|
1157
|
+
fileSources[sourcePath] = source;
|
|
1158
|
+
coverageData[sourcePath] = istanbulData;
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
return {
|
|
1162
|
+
v8DataList,
|
|
1163
|
+
fileSources,
|
|
1164
|
+
coverageData
|
|
1165
|
+
};
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
const convertV8List = (v8list, options) => {
|
|
1169
|
+
|
|
1170
|
+
const stateList = [];
|
|
921
1171
|
|
|
922
1172
|
for (const item of v8list) {
|
|
923
1173
|
// console.log([item.id]);
|
|
@@ -930,14 +1180,11 @@ const convertV8List = async (v8list, options) => {
|
|
|
930
1180
|
const js = type === 'js';
|
|
931
1181
|
item.js = js;
|
|
932
1182
|
|
|
933
|
-
// append file source
|
|
934
|
-
fileSources[sourcePath] = source;
|
|
935
|
-
|
|
936
1183
|
// source mapping
|
|
937
1184
|
const locator = new Locator(source);
|
|
938
1185
|
|
|
939
1186
|
// ============================
|
|
940
|
-
// move
|
|
1187
|
+
// move sourceMap
|
|
941
1188
|
const sourceMap = item.sourceMap;
|
|
942
1189
|
if (sourceMap) {
|
|
943
1190
|
delete item.sourceMap;
|
|
@@ -948,7 +1195,7 @@ const convertV8List = async (v8list, options) => {
|
|
|
948
1195
|
let coverageList = [];
|
|
949
1196
|
let astInfo;
|
|
950
1197
|
if (js) {
|
|
951
|
-
coverageList = item
|
|
1198
|
+
coverageList = filterCoverageList(item);
|
|
952
1199
|
// remove original functions
|
|
953
1200
|
if (Util.loggingType !== 'debug') {
|
|
954
1201
|
delete item.functions;
|
|
@@ -966,57 +1213,34 @@ const convertV8List = async (v8list, options) => {
|
|
|
966
1213
|
// ============================
|
|
967
1214
|
|
|
968
1215
|
// current file and it's sources from sourceMap
|
|
1216
|
+
// see const originalState
|
|
969
1217
|
const state = {
|
|
970
1218
|
js,
|
|
971
1219
|
type,
|
|
1220
|
+
source,
|
|
972
1221
|
sourcePath,
|
|
973
1222
|
sourceMap,
|
|
974
|
-
coverageList,
|
|
975
1223
|
locator,
|
|
1224
|
+
decodedMappings: [],
|
|
976
1225
|
// coverage info
|
|
977
1226
|
bytes: [],
|
|
978
1227
|
functions: [],
|
|
979
1228
|
branches: [],
|
|
980
1229
|
astInfo,
|
|
981
|
-
// for
|
|
982
|
-
|
|
983
|
-
|
|
1230
|
+
// for sub source files
|
|
1231
|
+
coverageList,
|
|
1232
|
+
originalList: [],
|
|
1233
|
+
// coverage data
|
|
1234
|
+
v8Data: item
|
|
984
1235
|
};
|
|
985
1236
|
|
|
986
|
-
|
|
1237
|
+
unpackDistFile(item, state, options);
|
|
987
1238
|
|
|
988
|
-
|
|
989
|
-
Object.assign(fileSources, state.fileSources);
|
|
990
|
-
Object.assign(coverageData, state.coverageData);
|
|
991
|
-
|
|
992
|
-
if (Util.isList(state.sourceList)) {
|
|
993
|
-
sourceList = sourceList.concat(state.sourceList);
|
|
994
|
-
}
|
|
1239
|
+
stateList.push(state);
|
|
995
1240
|
|
|
996
1241
|
}
|
|
997
1242
|
|
|
998
|
-
|
|
999
|
-
if (sourceList.length) {
|
|
1000
|
-
sourceList.forEach((item) => {
|
|
1001
|
-
|
|
1002
|
-
// second time filter for empty source
|
|
1003
|
-
// exists same id, mark previous item as dedupe
|
|
1004
|
-
const prevItem = v8list.find((it) => it.id === item.id);
|
|
1005
|
-
if (prevItem) {
|
|
1006
|
-
prevItem.dedupe = true;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
v8list.push(item);
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// dedupe
|
|
1014
|
-
dedupeV8List(v8list);
|
|
1015
|
-
|
|
1016
|
-
return {
|
|
1017
|
-
fileSources,
|
|
1018
|
-
coverageData
|
|
1019
|
-
};
|
|
1243
|
+
return generateV8DataList(stateList, options);
|
|
1020
1244
|
|
|
1021
1245
|
};
|
|
1022
1246
|
|