monocart-coverage-reports 1.1.0 → 2.0.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 +116 -29
- package/lib/converter/ast.js +297 -0
- package/lib/converter/branches.js +408 -0
- package/lib/converter/converter.js +720 -237
- package/lib/converter/find-original-range.js +106 -60
- package/lib/converter/info-branch.js +21 -22
- package/lib/converter/info-function.js +6 -18
- package/lib/converter/info-line.js +3 -0
- package/lib/generate.js +4 -0
- package/lib/istanbul/istanbul.js +1 -1
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +21 -14
- package/lib/platform/share.js +104 -3
- package/lib/{converter → utils}/source-path.js +2 -2
- package/lib/utils/util.js +27 -0
- package/lib/v8/v8-summary.js +19 -24
- package/lib/v8/v8.js +66 -28
- package/package.json +24 -29
- package/lib/converter/position-mapping.js +0 -183
- /package/lib/{converter → utils}/dedupe.js +0 -0
package/README.md
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
## Preview Reports
|
|
10
10
|
- [V8](https://cenfun.github.io/monocart-coverage-reports/v8)
|
|
11
|
+
- [V8 Esbuild](https://cenfun.github.io/monocart-coverage-reports/v8-esbuild)
|
|
12
|
+
- [V8 Rollup](https://cenfun.github.io/monocart-coverage-reports/v8-rollup)
|
|
13
|
+
- [V8 lcov.info](https://cenfun.github.io/monocart-coverage-reports/v8/lcov.info)
|
|
14
|
+
- [V8 Node env](https://cenfun.github.io/monocart-coverage-reports/v8-node-env)
|
|
15
|
+
- [V8 Node Inspector](https://cenfun.github.io/monocart-coverage-reports/v8-node-ins)
|
|
11
16
|
- [V8 Minify](https://cenfun.github.io/monocart-coverage-reports/v8-minify)
|
|
12
17
|
- [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
|
|
13
18
|
- [Istanbul](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
|
|
@@ -26,30 +31,30 @@ await coverageReport.add(coverageData2);
|
|
|
26
31
|
const coverageResults = await coverageReport.generate();
|
|
27
32
|
console.log(coverageResults.summary);
|
|
28
33
|
```
|
|
29
|
-
- [example v8](https://github.com/cenfun/monocart-coverage-reports/blob/main/
|
|
30
|
-
- [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/
|
|
34
|
+
- [example v8](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js)
|
|
35
|
+
- [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
|
|
31
36
|
|
|
32
37
|
## Default Options
|
|
33
38
|
- [lib/default/options.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/lib/default/options.js)
|
|
34
39
|
|
|
35
40
|
## Multiple Reports
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
### V8 reports
|
|
42
|
+
- v8 (html)
|
|
43
|
+
- v8-json
|
|
44
|
+
### [Istanbul reports](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib)
|
|
45
|
+
- clover
|
|
46
|
+
- cobertura
|
|
47
|
+
- html
|
|
48
|
+
- html-spa
|
|
49
|
+
- json
|
|
50
|
+
- json-summary
|
|
51
|
+
- lcov
|
|
52
|
+
- lcovonly
|
|
53
|
+
- none
|
|
54
|
+
- teamcity
|
|
55
|
+
- text
|
|
56
|
+
- text-lcov
|
|
57
|
+
- text-summary
|
|
53
58
|
|
|
54
59
|
```js
|
|
55
60
|
const CoverageReport = require('monocart-coverage-reports');
|
|
@@ -69,7 +74,7 @@ const options = {
|
|
|
69
74
|
const coverageReport = new CoverageReport(options);
|
|
70
75
|
```
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
## Multiprocessing Support
|
|
73
78
|
The data will be added to `[outputDir]/.cache`, and the cache will be removed after reports generated.
|
|
74
79
|
- sub process 1
|
|
75
80
|
```js
|
|
@@ -100,24 +105,29 @@ console.log(coverageResults.summary);
|
|
|
100
105
|
## Compare Reports
|
|
101
106
|
| | Istanbul | V8 | V8 to Istanbul |
|
|
102
107
|
| :--------------| :------ | :------ | :---------------------- |
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
108
|
+
| Instrumenting code | Required | No | No |
|
|
109
|
+
| Coverage data | Istanbul (Object) | V8 (Array) | V8 (Array) |
|
|
110
|
+
| Output | [Istanbul reports](#istanbul-reports) | [V8 reports](#v8-reports) | [Istanbul reports](#istanbul-reports) |
|
|
111
|
+
| - Bytes | ❌ | ✅ | ❌ |
|
|
112
|
+
| - Statements | ✅ | ❌ | ☑️❔ |
|
|
113
|
+
| - Branches | ✅ | ☑️❔ | ☑️❔ |
|
|
114
|
+
| - Functions | ✅ | ✅ | ✅ |
|
|
115
|
+
| - Lines | ✅ | ✅ | ✅ |
|
|
116
|
+
| - Execution counts | ✅ | ✅ | ✅ |
|
|
106
117
|
| CSS coverage | ❌ | ✅ | ✅ |
|
|
107
|
-
| Minified code |
|
|
108
|
-
| Code formatting | N/A | ✅ | ❌ |
|
|
118
|
+
| Minified code | ❌ | ✅ | ❌ |
|
|
109
119
|
|
|
110
120
|
❔ - Partial or conditional support
|
|
111
121
|
|
|
112
122
|
## Compare Workflows
|
|
113
123
|
- Istanbul Workflows
|
|
114
|
-
- 1, Only for source code: instrumenting code with [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul),see [webpack.config-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/
|
|
115
|
-
- 2, Collecting coverage data from browser `window.__coverage__`, see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/
|
|
124
|
+
- 1, Only for source code: instrumenting code with [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul),see [webpack.config-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-istanbul.js), or using [nyc](https://github.com/istanbuljs/nyc).
|
|
125
|
+
- 2, Collecting coverage data from browser `window.__coverage__` or from node `global.__coverage__`, see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js).
|
|
116
126
|
- 3, Generating coverage report.
|
|
117
127
|
|
|
118
128
|
- V8 Workflows
|
|
119
|
-
- 1, For any runtime code: nothing to do. For source code: building code with `development` [mode](https://webpack.js.org/configuration/mode/) and [sourcemap](https://webpack.js.org/configuration/devtool/) support, see [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/
|
|
120
|
-
- 2, Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api), see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/
|
|
129
|
+
- 1, For any runtime code: nothing to do. For source code: building code with `development` [mode](https://webpack.js.org/configuration/mode/) and [sourcemap](https://webpack.js.org/configuration/devtool/) support, see [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-v8.js).
|
|
130
|
+
- 2, Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api), see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js).
|
|
121
131
|
- 3, Generating coverage report.
|
|
122
132
|
|
|
123
133
|
## Chromium Coverage API
|
|
@@ -127,11 +137,88 @@ console.log(coverageResults.summary);
|
|
|
127
137
|
- [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
|
|
128
138
|
|
|
129
139
|
|
|
140
|
+
## Node.js V8 Coverage Report for Server Side
|
|
141
|
+
There are two ways to collect native V8 coverage data and generate coverage report:
|
|
142
|
+
- Using Node.js env [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
|
|
143
|
+
- 1, Before running your Node.js application, set env `NODE_V8_COVERAGE`=`dir`. After the application runs and exits, the coverage data will be saved to the `dir` directory in JSON file format.
|
|
144
|
+
- 2, Read the json file(s) from the `dir` and generate coverage report. see example:
|
|
145
|
+
> cross-env NODE_V8_COVERAGE=`.temp/v8-coverage` node [./test/test-node-env.js](./test/test-node-env.js) && node [./test/generate-node-report.js](./test/generate-node-report.js)
|
|
146
|
+
|
|
147
|
+
- Using [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API
|
|
148
|
+
- 1, Connecting to the V8 inspector and enable V8 coverage.
|
|
149
|
+
```js
|
|
150
|
+
const inspector = require('inspector');
|
|
151
|
+
const startV8Coverage = async () => {
|
|
152
|
+
const session = new inspector.Session();
|
|
153
|
+
session.connect();
|
|
154
|
+
await session.post('Profiler.enable');
|
|
155
|
+
await session.post('Profiler.startPreciseCoverage', {
|
|
156
|
+
callCount: true,
|
|
157
|
+
detailed: true
|
|
158
|
+
});
|
|
159
|
+
return session;
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
- 2, Taking coverage data and adding to the report after your application runs.
|
|
163
|
+
```js
|
|
164
|
+
const takeV8Coverage = (session) => {
|
|
165
|
+
return new Promise((resolve) => {
|
|
166
|
+
session.post('Profiler.takePreciseCoverage', (error, coverage) => {
|
|
167
|
+
if (error) {
|
|
168
|
+
console.log(error);
|
|
169
|
+
resolve();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
resolve(coverage.result);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
const coverageList = await takeV8Coverage(session);
|
|
177
|
+
// filter node internal files
|
|
178
|
+
coverageList = coverageList.filter((entry) => entry.url && !entry.url.startsWith('node:'));
|
|
179
|
+
await new CoverageReport(coverageOptions).add(coverageList);
|
|
180
|
+
```
|
|
181
|
+
- 3, Generating coverage report. see example: [./test/test-node-ins.js](./test/test-node-ins.js)
|
|
182
|
+
```js
|
|
183
|
+
await new CoverageReport(coverageOptions).generate();
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
|
|
187
|
+
When you add coverage data with [Chromium Coverage API](#chromium-coverage-api), it actually contains the data of all entry files, for example:
|
|
188
|
+
```
|
|
189
|
+
1, dist/main.js
|
|
190
|
+
2, dist/vendor.js
|
|
191
|
+
3, dist/something-else.js
|
|
192
|
+
```
|
|
193
|
+
We can use `entryFilter` to filter the entry files. For example, we should remove `vendor.js` and `something-else.js` if they are not in our coverage scope.
|
|
194
|
+
```
|
|
195
|
+
1, dist/main.js
|
|
196
|
+
```
|
|
197
|
+
When inline or linked sourcemap exists to the entry file, the source files will be extracted from the sourcemap for the entry file, and the entry file will be removed if `logging` is not `debug`.
|
|
198
|
+
```
|
|
199
|
+
1, src/index.js
|
|
200
|
+
2, src/components/app.js
|
|
201
|
+
3, node_modules/dependency/dist/dependency.js
|
|
202
|
+
```
|
|
203
|
+
We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
|
|
204
|
+
```
|
|
205
|
+
1, src/index.js
|
|
206
|
+
2, src/components/app.js
|
|
207
|
+
```
|
|
208
|
+
Example:
|
|
209
|
+
```js
|
|
210
|
+
const coverageOptions = {
|
|
211
|
+
entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
|
|
212
|
+
sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
130
216
|
## Istanbul Coverage
|
|
131
217
|
- [Istanbul coverage report](https://istanbul.js.org/) - Instrumenting source codes and generating coverage reports
|
|
132
218
|
- [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul)
|
|
133
219
|
- [istanbul-reports](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib)
|
|
134
220
|
- [Code Coverage Introduction](https://docs.cypress.io/guides/tooling/code-coverage)
|
|
135
221
|
|
|
222
|
+
|
|
136
223
|
## Thanks
|
|
137
224
|
- Special thanks to [@edumserrano](https://github.com/edumserrano)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
const {
|
|
2
|
+
acorn, acornLoose, parseCss
|
|
3
|
+
} = require('../packages/monocart-coverage-vendor.js');
|
|
4
|
+
const Util = require('../utils/util.js');
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
createBranches, collectBranches, findInRanges
|
|
8
|
+
} = require('./branches.js');
|
|
9
|
+
|
|
10
|
+
const getFunctionRange = (start, end, state) => {
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
functionMap, functionNameMap, functionRanges
|
|
14
|
+
} = state;
|
|
15
|
+
|
|
16
|
+
if (!functionRanges.length) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// exact matched in functionMap
|
|
21
|
+
const range = functionMap.get(start);
|
|
22
|
+
if (range) {
|
|
23
|
+
return range;
|
|
24
|
+
}
|
|
25
|
+
// exact matched in functionNameMap
|
|
26
|
+
const nameRange = functionNameMap.get(start);
|
|
27
|
+
if (nameRange) {
|
|
28
|
+
return nameRange;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// find in functionRanges
|
|
32
|
+
return findInRanges(start, end, functionRanges);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const collectAstInfo = (ast, state) => {
|
|
36
|
+
|
|
37
|
+
const functions = [];
|
|
38
|
+
const branchMap = new Map();
|
|
39
|
+
|
|
40
|
+
Util.visitAst(ast, {
|
|
41
|
+
|
|
42
|
+
VariableDeclarator(node, parents) {
|
|
43
|
+
// id: { type: 'Identifier', name: '__webpack_modules__' },
|
|
44
|
+
const name = node.id && node.id.name;
|
|
45
|
+
if (name === '__webpack_modules__' && node.init) {
|
|
46
|
+
// mark all as wrap function
|
|
47
|
+
node.init.properties.forEach((it) => {
|
|
48
|
+
it.value._wrapKey = it.key && it.key.value;
|
|
49
|
+
});
|
|
50
|
+
// console.log('==========================', node.type);
|
|
51
|
+
// console.log(name, parents.length);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// Function include FunctionDeclaration, ArrowFunctionExpression, FunctionExpression
|
|
56
|
+
Function(node) {
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
start, end, id
|
|
60
|
+
} = node;
|
|
61
|
+
const functionName = id && id.name;
|
|
62
|
+
|
|
63
|
+
const functionInfo = {
|
|
64
|
+
start,
|
|
65
|
+
end,
|
|
66
|
+
functionName,
|
|
67
|
+
// js default to 1
|
|
68
|
+
count: 1
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (node._wrapKey) {
|
|
72
|
+
functionInfo.wrap = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const range = getFunctionRange(start, end, state);
|
|
76
|
+
if (range) {
|
|
77
|
+
functionInfo.count = range.count;
|
|
78
|
+
functionInfo.range = range;
|
|
79
|
+
// set wrap back to range
|
|
80
|
+
if (functionInfo.wrap) {
|
|
81
|
+
range.wrap = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
functions.push(functionInfo);
|
|
86
|
+
|
|
87
|
+
createBranches(node.body, functionInfo, branchMap);
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const branches = collectBranches(branchMap);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
functions,
|
|
97
|
+
branches
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const generateCountState = (coverageList) => {
|
|
103
|
+
const functionRanges = [];
|
|
104
|
+
const functionNameMap = new Map();
|
|
105
|
+
coverageList.forEach((block) => {
|
|
106
|
+
const { functionName, ranges } = block;
|
|
107
|
+
|
|
108
|
+
// first one is function coverage info
|
|
109
|
+
const functionRange = ranges[0];
|
|
110
|
+
functionRange.functionName = functionName;
|
|
111
|
+
if (functionName) {
|
|
112
|
+
functionNameMap.set(functionRange.startOffset + functionName.length, functionRange);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// blocks
|
|
116
|
+
const len = ranges.length;
|
|
117
|
+
if (len > 1) {
|
|
118
|
+
const blockRanges = [].concat(ranges);
|
|
119
|
+
// remove first one
|
|
120
|
+
blockRanges.shift();
|
|
121
|
+
blockRanges.sort((a, b) => {
|
|
122
|
+
return a.startOffset - b.startOffset;
|
|
123
|
+
});
|
|
124
|
+
const blockMap = new Map();
|
|
125
|
+
blockRanges.forEach((item) => {
|
|
126
|
+
blockMap.set(item.startOffset, item);
|
|
127
|
+
});
|
|
128
|
+
functionRange.blockMap = blockMap;
|
|
129
|
+
functionRange.blockRanges = blockRanges;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
functionRanges.push(functionRange);
|
|
133
|
+
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// sort ranges
|
|
137
|
+
functionRanges.sort((a, b) => {
|
|
138
|
+
return a.startOffset - b.startOffset;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// keep map index for exact search
|
|
142
|
+
const functionMap = new Map();
|
|
143
|
+
functionRanges.forEach((item) => {
|
|
144
|
+
functionMap.set(item.startOffset, item);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
functionMap,
|
|
149
|
+
functionNameMap,
|
|
150
|
+
functionRanges
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const getJsAstInfo = (item, coverageList) => {
|
|
156
|
+
|
|
157
|
+
// not for css
|
|
158
|
+
if (!item.js) {
|
|
159
|
+
return {
|
|
160
|
+
functions: [],
|
|
161
|
+
branches: []
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const { source } = item;
|
|
166
|
+
|
|
167
|
+
const options = {
|
|
168
|
+
ecmaVersion: 'latest',
|
|
169
|
+
// most time for node.js file
|
|
170
|
+
allowReturnOutsideFunction: true,
|
|
171
|
+
allowImportExportEverywhere: true,
|
|
172
|
+
allowSuperOutsideMethod: true,
|
|
173
|
+
// first line: #!/usr/bin/env node
|
|
174
|
+
allowHashBang: true
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
let ast;
|
|
178
|
+
try {
|
|
179
|
+
ast = acorn.parse(source, options);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
Util.logError(`failed to parse source file and fall back to loose: ${item.sourcePath} ${e.message}`);
|
|
182
|
+
// https://github.com/acornjs/acorn/tree/master/acorn-loose
|
|
183
|
+
// It is recommended to always try a parse with the regular acorn parser first,
|
|
184
|
+
// and only fall back to this parser when that one finds syntax errors.
|
|
185
|
+
ast = acornLoose.parse(source, options);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const state = generateCountState(coverageList);
|
|
189
|
+
// state.item = item;
|
|
190
|
+
|
|
191
|
+
return collectAstInfo(ast, state);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// =========================================================================================================
|
|
195
|
+
|
|
196
|
+
const addCssRules = (rules, coverageList, coveredRanges, locator) => {
|
|
197
|
+
let hasCovered = false;
|
|
198
|
+
// line and line is 1-base
|
|
199
|
+
rules.forEach((rule) => {
|
|
200
|
+
|
|
201
|
+
const { type, position } = rule;
|
|
202
|
+
|
|
203
|
+
if (type === 'comment') {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const { start, end } = position;
|
|
208
|
+
|
|
209
|
+
// both 1-base
|
|
210
|
+
// console.log(start, end);
|
|
211
|
+
const startOffset = locator.locationToOffset({
|
|
212
|
+
line: start.line,
|
|
213
|
+
column: start.column - 1
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const endOffset = locator.locationToOffset({
|
|
217
|
+
line: end.line,
|
|
218
|
+
column: end.column - 1
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// start should offset @media
|
|
222
|
+
if (type === 'media') {
|
|
223
|
+
// rules in media
|
|
224
|
+
// console.log('media', rule.media, startOffset, endOffset);
|
|
225
|
+
const childCoverageList = [];
|
|
226
|
+
const hasChildCovered = addCssRules(rule.rules, childCoverageList, coveredRanges, locator);
|
|
227
|
+
if (hasChildCovered) {
|
|
228
|
+
childCoverageList.forEach((it) => {
|
|
229
|
+
coverageList.push(it);
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// add whole media as uncovered
|
|
235
|
+
coverageList.push({
|
|
236
|
+
start: startOffset,
|
|
237
|
+
end: endOffset,
|
|
238
|
+
count: 0
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let count = 0;
|
|
244
|
+
if (type === 'charset') {
|
|
245
|
+
count = 1;
|
|
246
|
+
} else {
|
|
247
|
+
const coveredRange = findInRanges(startOffset, endOffset, coveredRanges);
|
|
248
|
+
if (coveredRange) {
|
|
249
|
+
count = 1;
|
|
250
|
+
hasCovered = true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
coverageList.push({
|
|
255
|
+
start: startOffset,
|
|
256
|
+
end: endOffset,
|
|
257
|
+
count
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return hasCovered;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const getCssAstInfo = (item, locator) => {
|
|
266
|
+
|
|
267
|
+
const {
|
|
268
|
+
source, sourcePath, ranges
|
|
269
|
+
} = item;
|
|
270
|
+
|
|
271
|
+
// sort covered ranges
|
|
272
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
273
|
+
// to offset covered ranges
|
|
274
|
+
const coveredRanges = ranges.map((it) => {
|
|
275
|
+
return {
|
|
276
|
+
startOffset: it.start,
|
|
277
|
+
endOffset: it.end
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const time_start_parse = new Date();
|
|
282
|
+
const ast = parseCss(source);
|
|
283
|
+
Util.logTime(`parse css: ${sourcePath}`, time_start_parse);
|
|
284
|
+
|
|
285
|
+
const coverageList = [];
|
|
286
|
+
addCssRules(ast.rules, coverageList, coveredRanges, locator);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
coverageList
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
module.exports = {
|
|
295
|
+
getJsAstInfo,
|
|
296
|
+
getCssAstInfo
|
|
297
|
+
};
|