monocart-coverage-reports 2.13.1 → 2.13.2
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.
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const EC = require('eight-colors');
|
|
4
|
-
const { CommentParser } = require('monocart-locator');
|
|
5
4
|
const { fileURLToPath, pathToFileURL } = require('url');
|
|
6
5
|
const Concurrency = require('../platform/concurrency.js');
|
|
7
6
|
const { convertSourceMap } = require('../packages/monocart-coverage-vendor.js');
|
|
@@ -9,15 +8,72 @@ const { flattenSourceMaps } = require('./flatten-source-maps.js');
|
|
|
9
8
|
|
|
10
9
|
const Util = require('../utils/util.js');
|
|
11
10
|
|
|
12
|
-
const
|
|
11
|
+
const getSourceMapCandidates = (source) => {
|
|
13
12
|
if (!source.includes('sourceMappingURL=')) {
|
|
14
13
|
return [];
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
|
|
16
|
+
// Match directive-shaped text without parsing JS; valid maps in strings can be selected.
|
|
17
|
+
const candidates = [];
|
|
18
|
+
for (const match of source.matchAll(convertSourceMap.commentRegex)) {
|
|
19
|
+
candidates.push({
|
|
20
|
+
index: match.index,
|
|
21
|
+
comment: match[0],
|
|
22
|
+
inline: true
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
for (const match of source.matchAll(convertSourceMap.mapFileCommentRegex)) {
|
|
26
|
+
const filename = match[1] || match[2];
|
|
27
|
+
if (filename && !filename.startsWith('data:')) {
|
|
28
|
+
candidates.push({
|
|
29
|
+
index: match.index,
|
|
30
|
+
filename
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Like 2.13.0, try inline maps before external ones. Within each kind,
|
|
35
|
+
// start at the last directive and fall back when it is unusable.
|
|
36
|
+
return candidates.sort((a, b) => {
|
|
37
|
+
if (a.inline !== b.inline) {
|
|
38
|
+
return a.inline ? -1 : 1;
|
|
39
|
+
}
|
|
40
|
+
return b.index - a.index;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Only check the fields required to resolve a map; decoding is deferred to
|
|
45
|
+
// the downstream converter. A map without sources cannot map to original code.
|
|
46
|
+
const isValidBasicMap = (data) => Array.isArray(data.sources)
|
|
47
|
+
&& data.sources.every((source) => typeof source === 'string')
|
|
48
|
+
&& typeof data.mappings === 'string'
|
|
49
|
+
&& (data.sources.length > 0 || data.mappings === '');
|
|
50
|
+
|
|
51
|
+
const isValidSection = (section) => section && section.offset
|
|
52
|
+
&& Number.isInteger(section.offset.line) && section.offset.line >= 0
|
|
53
|
+
&& Number.isInteger(section.offset.column) && section.offset.column >= 0
|
|
54
|
+
&& isValidSourceMap(section.map);
|
|
55
|
+
|
|
56
|
+
const isValidSourceMap = (data) => {
|
|
57
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(data.sections)) {
|
|
61
|
+
return data.sections.every(isValidSection);
|
|
62
|
+
}
|
|
63
|
+
return isValidBasicMap(data);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// An empty map cannot map coverage to any original source. Try the next
|
|
67
|
+
// candidate instead, including when an indexed map has only empty sections.
|
|
68
|
+
const hasMappings = (data) => {
|
|
69
|
+
if (Array.isArray(data.sections)) {
|
|
70
|
+
return data.sections.some((section) => hasMappings(section.map));
|
|
71
|
+
}
|
|
72
|
+
return data.mappings.length > 0;
|
|
19
73
|
};
|
|
20
74
|
|
|
75
|
+
const isUsableSourceMap = (data) => isValidSourceMap(data) && hasMappings(data);
|
|
76
|
+
|
|
21
77
|
const defaultSourceMapResolver = async (url = '') => {
|
|
22
78
|
|
|
23
79
|
if (url.startsWith('file:')) {
|
|
@@ -63,24 +119,6 @@ const resolveMapUrl = (filename, url) => {
|
|
|
63
119
|
return urlObj?.toString();
|
|
64
120
|
};
|
|
65
121
|
|
|
66
|
-
const getSourceMapUrl = (comments, url) => {
|
|
67
|
-
for (let i = comments.length - 1; i >= 0; i--) {
|
|
68
|
-
const r = convertSourceMap.mapFileCommentRegex.exec(comments[i]);
|
|
69
|
-
if (!r || r.index !== 0) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
// //# captures in 1 and /* */ in 2
|
|
73
|
-
const filename = r[1] || r[2];
|
|
74
|
-
if (!filename || filename.startsWith('data:')) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const mapUrl = resolveMapUrl(filename, url);
|
|
78
|
-
if (mapUrl) {
|
|
79
|
-
return mapUrl;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
122
|
const resolveSourcesContent = (data, url) => {
|
|
85
123
|
|
|
86
124
|
const { sources, sourcesContent } = data;
|
|
@@ -125,7 +163,7 @@ const resolveSourcesContent = (data, url) => {
|
|
|
125
163
|
const checkSourcesContent = (data) => {
|
|
126
164
|
const { sourcesContent, sources } = data;
|
|
127
165
|
|
|
128
|
-
if (!sourcesContent) {
|
|
166
|
+
if (!Array.isArray(sourcesContent)) {
|
|
129
167
|
data.sourcesContent = [];
|
|
130
168
|
return false;
|
|
131
169
|
}
|
|
@@ -146,15 +184,7 @@ const resolveSectionedSourceMap = (data, url, sections) => {
|
|
|
146
184
|
// offset: { line: 1, column: 0 },
|
|
147
185
|
// map: { sources, sourcesContent }
|
|
148
186
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (checkSourcesContent(map)) {
|
|
152
|
-
hasSourceContent = true;
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const done = resolveSourcesContent(map, url);
|
|
157
|
-
if (done) {
|
|
187
|
+
if (resolveSourceMap(item.map, url)) {
|
|
158
188
|
hasSourceContent = true;
|
|
159
189
|
}
|
|
160
190
|
|
|
@@ -169,6 +199,10 @@ const resolveSourceMap = (data, url) => {
|
|
|
169
199
|
if (!data) {
|
|
170
200
|
return;
|
|
171
201
|
}
|
|
202
|
+
// Optional metadata may be absent or malformed in otherwise usable maps.
|
|
203
|
+
if (!Array.isArray(data.names)) {
|
|
204
|
+
data.names = [];
|
|
205
|
+
}
|
|
172
206
|
const {
|
|
173
207
|
sections, sources, mappings
|
|
174
208
|
} = data;
|
|
@@ -177,7 +211,7 @@ const resolveSourceMap = (data, url) => {
|
|
|
177
211
|
return resolveSectionedSourceMap(data, url, sections);
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
if (!sources ||
|
|
214
|
+
if (!Array.isArray(sources) || typeof mappings !== 'string') {
|
|
181
215
|
return;
|
|
182
216
|
}
|
|
183
217
|
|
|
@@ -191,21 +225,6 @@ const resolveSourceMap = (data, url) => {
|
|
|
191
225
|
|
|
192
226
|
};
|
|
193
227
|
|
|
194
|
-
const getInlineSourceMap = (comments) => {
|
|
195
|
-
for (let i = comments.length - 1; i >= 0; i--) {
|
|
196
|
-
const comment = comments[i];
|
|
197
|
-
const match = convertSourceMap.commentRegex.exec(comment);
|
|
198
|
-
if (!match || match.index !== 0) {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
return convertSourceMap.fromComment(comment);
|
|
203
|
-
} catch (e) {
|
|
204
|
-
// Ignore invalid inline maps.
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
|
|
209
228
|
const collectSourceMaps = async (v8list, options) => {
|
|
210
229
|
|
|
211
230
|
const sourceList = [];
|
|
@@ -237,52 +256,60 @@ const collectSourceMaps = async (v8list, options) => {
|
|
|
237
256
|
sourceMap
|
|
238
257
|
};
|
|
239
258
|
|
|
259
|
+
// An explicitly supplied, usable map takes precedence over sourceMappingURL.
|
|
240
260
|
// Keep the original source so V8 offsets remain valid.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
const smc = getInlineSourceMap(comments);
|
|
246
|
-
if (smc) {
|
|
247
|
-
sourceData.sourceMap = resolveSourceMap(smc.sourcemap, url);
|
|
248
|
-
sourcemapList.push({
|
|
249
|
-
url,
|
|
250
|
-
inline: true
|
|
251
|
-
});
|
|
252
|
-
sourceList.push(sourceData);
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
// from url async
|
|
256
|
-
const sourceMapUrl = getSourceMapUrl(comments, url);
|
|
257
|
-
if (sourceMapUrl) {
|
|
261
|
+
if (type === 'js' && !isUsableSourceMap(sourceData.sourceMap)) {
|
|
262
|
+
delete sourceData.sourceMap;
|
|
263
|
+
const candidates = getSourceMapCandidates(source);
|
|
264
|
+
if (candidates.length) {
|
|
258
265
|
concurrency.addItem({
|
|
259
|
-
|
|
266
|
+
candidates,
|
|
260
267
|
sourceData
|
|
261
268
|
});
|
|
262
|
-
continue;
|
|
263
269
|
}
|
|
264
270
|
}
|
|
265
271
|
|
|
266
|
-
//
|
|
272
|
+
// Keep source entries in input order while maps load concurrently.
|
|
267
273
|
sourceList.push(sourceData);
|
|
268
274
|
|
|
269
275
|
}
|
|
270
276
|
|
|
271
|
-
// from url concurrency
|
|
272
277
|
await concurrency.start(async (item) => {
|
|
273
|
-
const {
|
|
278
|
+
const { candidates, sourceData } = item;
|
|
274
279
|
const { url } = sourceData;
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
for (const candidate of candidates) {
|
|
281
|
+
let data;
|
|
282
|
+
let sourceMapUrl;
|
|
283
|
+
try {
|
|
284
|
+
if (candidate.inline) {
|
|
285
|
+
data = convertSourceMap.fromComment(candidate.comment).sourcemap;
|
|
286
|
+
} else {
|
|
287
|
+
sourceMapUrl = resolveMapUrl(candidate.filename, url);
|
|
288
|
+
if (!sourceMapUrl) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
data = await loadSourceMap(sourceMapUrl, options);
|
|
292
|
+
}
|
|
293
|
+
if (!isUsableSourceMap(data)) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const sourceMap = resolveSourceMap(data, url);
|
|
297
|
+
if (!sourceMap) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
sourceData.sourceMap = sourceMap;
|
|
301
|
+
sourcemapList.push(candidate.inline ? {
|
|
302
|
+
url,
|
|
303
|
+
inline: true
|
|
304
|
+
} : {
|
|
305
|
+
url,
|
|
306
|
+
sourceMapUrl
|
|
307
|
+
});
|
|
308
|
+
break;
|
|
309
|
+
} catch (e) {
|
|
310
|
+
// Try an earlier sourceMappingURL if parsing or loading fails.
|
|
311
|
+
}
|
|
282
312
|
}
|
|
283
|
-
|
|
284
|
-
sourceList.push(sourceData);
|
|
285
|
-
|
|
286
313
|
});
|
|
287
314
|
|
|
288
315
|
return {
|
|
@@ -906,6 +906,12 @@ const decodeSourceMappings = (state, originalDecodedMap) => {
|
|
|
906
906
|
// console.log(segment);
|
|
907
907
|
return;
|
|
908
908
|
}
|
|
909
|
+
// A malformed map may refer to a source outside its sources array.
|
|
910
|
+
// Check when mappings are already decoded, not during map selection.
|
|
911
|
+
const sourceMappings = originalDecodedMap.get(sourceIndex);
|
|
912
|
+
if (!sourceMappings) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
909
915
|
|
|
910
916
|
// 1-base
|
|
911
917
|
const generatedSN = generatedLine + 1;
|
|
@@ -936,7 +942,7 @@ const decodeSourceMappings = (state, originalDecodedMap) => {
|
|
|
936
942
|
}
|
|
937
943
|
|
|
938
944
|
allDecodedMappings.push(info);
|
|
939
|
-
|
|
945
|
+
sourceMappings.push(info);
|
|
940
946
|
|
|
941
947
|
// calculate line end column
|
|
942
948
|
if (!info.last) {
|
|
@@ -971,7 +977,7 @@ const decodeSourceMappings = (state, originalDecodedMap) => {
|
|
|
971
977
|
// console.log(generatedSN, generatedColumn, info, endColumn);
|
|
972
978
|
|
|
973
979
|
allDecodedMappings.push(endColumn);
|
|
974
|
-
|
|
980
|
+
sourceMappings.push(endColumn);
|
|
975
981
|
|
|
976
982
|
});
|
|
977
983
|
|