@teamscale/lib-instrument 1.0.0-beta.6 → 1.0.4
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/lib/index.cjs +613 -0
- package/lib/index.d.cts +45 -0
- package/lib/index.d.cts.map +1 -0
- package/lib/index.d.mts +45 -0
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +613 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +26 -12
- package/lib/index.d.ts +0 -10
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -15
- package/lib/instrumenter.d.ts +0 -45
- package/lib/instrumenter.d.ts.map +0 -1
- package/lib/instrumenter.js +0 -91
- package/lib/origins.d.ts +0 -32
- package/lib/origins.d.ts.map +0 -1
- package/lib/origins.js +0 -72
- package/lib/utils.d.ts +0 -36
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js +0 -2
- package/lib/visitor.d.ts +0 -30
- package/lib/visitor.d.ts.map +0 -1
- package/lib/visitor.js +0 -562
package/lib/visitor.js
DELETED
|
@@ -1,562 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.programVisitor = programVisitor;
|
|
4
|
-
const core_1 = require("@babel/core");
|
|
5
|
-
const origins_1 = require("./origins");
|
|
6
|
-
// Pattern for istanbul to ignore a section
|
|
7
|
-
const COMMENT_RE = /^\s*istanbul\s+ignore\s+(if|else|next)(?=\W|$)/;
|
|
8
|
-
// Pattern for istanbul to ignore the whole file
|
|
9
|
-
const COMMENT_FILE_RE = /^\s*istanbul\s+ignore\s+(file)(?=\W|$)/;
|
|
10
|
-
/**
|
|
11
|
-
* `VisitState` holds the state of the visitor, provides helper functions
|
|
12
|
-
* and is the `this` for the individual coverage visitors.
|
|
13
|
-
*/
|
|
14
|
-
class VisitState {
|
|
15
|
-
constructor(types, inputSourceMapConsumer, ignoreClassMethods = [], reportLogic = false, shouldInstrumentCallback) {
|
|
16
|
-
this.attrs = {};
|
|
17
|
-
this.nextIgnore = null;
|
|
18
|
-
this.ignoreClassMethods = ignoreClassMethods;
|
|
19
|
-
this.types = types;
|
|
20
|
-
this.reportLogic = reportLogic;
|
|
21
|
-
this.shouldInstrumentCallback = shouldInstrumentCallback;
|
|
22
|
-
this.origins = new origins_1.SourceOrigins(inputSourceMapConsumer);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Use the configured callback, if available, to check if the given source
|
|
26
|
-
* location should be instrumented.
|
|
27
|
-
*/
|
|
28
|
-
shouldInstrument(path, loc) {
|
|
29
|
-
if (this.shouldInstrumentCallback) {
|
|
30
|
-
return this.shouldInstrumentCallback(path, loc);
|
|
31
|
-
}
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
/** Should we ignore the node? Yes, if specifically ignoring or if the node is generated. */
|
|
35
|
-
shouldIgnore(path) {
|
|
36
|
-
return this.nextIgnore !== null || !path.node.loc;
|
|
37
|
-
}
|
|
38
|
-
/** Extract the ignore comment hint (next|if|else) or null. */
|
|
39
|
-
hintFor(node) {
|
|
40
|
-
let hint = null;
|
|
41
|
-
if (node.leadingComments) {
|
|
42
|
-
node.leadingComments.forEach(c => {
|
|
43
|
-
const v = (c.value || '').trim();
|
|
44
|
-
const groups = v.match(COMMENT_RE);
|
|
45
|
-
if (groups) {
|
|
46
|
-
hint = groups[1];
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return hint;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* For these expressions the statement counter needs to be hoisted, so
|
|
54
|
-
* function name inference can be preserved.
|
|
55
|
-
*/
|
|
56
|
-
counterNeedsHoisting(path) {
|
|
57
|
-
return (path.isFunctionExpression() ||
|
|
58
|
-
path.isArrowFunctionExpression() ||
|
|
59
|
-
path.isClassExpression());
|
|
60
|
-
}
|
|
61
|
-
/** All the generic stuff that needs to be done on enter for every node. */
|
|
62
|
-
onEnter(path) {
|
|
63
|
-
const n = path.node;
|
|
64
|
-
// if already ignoring, nothing more to do
|
|
65
|
-
if (this.nextIgnore !== null) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// check hint to see if ignore should be turned on
|
|
69
|
-
const hint = this.hintFor(n);
|
|
70
|
-
if (hint === 'next') {
|
|
71
|
-
this.nextIgnore = n;
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
// else check custom node attribute set by a prior visitor
|
|
75
|
-
if (this.getAttr(path.node, 'skip-all') !== null) {
|
|
76
|
-
this.nextIgnore = n;
|
|
77
|
-
}
|
|
78
|
-
// else check for ignored class methods
|
|
79
|
-
if (path.isFunctionExpression() &&
|
|
80
|
-
this.ignoreClassMethods.some(name => path.node.id && name === path.node.id.name)) {
|
|
81
|
-
this.nextIgnore = n;
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (path.isClassMethod() &&
|
|
85
|
-
this.ignoreClassMethods.some(name => name === path.node.key.name)) {
|
|
86
|
-
this.nextIgnore = n;
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* All the generic stuff on exit of a node, including resetting ignores and custom node attrs.
|
|
92
|
-
*/
|
|
93
|
-
onExit(path) {
|
|
94
|
-
// restore ignore status, if needed
|
|
95
|
-
if (path.node === this.nextIgnore) {
|
|
96
|
-
this.nextIgnore = null;
|
|
97
|
-
}
|
|
98
|
-
// nuke all attributes for the node
|
|
99
|
-
delete path.node.__cov__;
|
|
100
|
-
}
|
|
101
|
-
/** Set a node attribute for the supplied node. */
|
|
102
|
-
setAttr(node, name, value) {
|
|
103
|
-
node.__cov__ = node.__cov__ || {};
|
|
104
|
-
node.__cov__[name] = value;
|
|
105
|
-
}
|
|
106
|
-
/** Retrieve a node attribute for the supplied node or null. */
|
|
107
|
-
getAttr(node, name) {
|
|
108
|
-
const c = node.__cov__;
|
|
109
|
-
if (!c) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
return c[name];
|
|
113
|
-
}
|
|
114
|
-
insertCounter(path, increment) {
|
|
115
|
-
var _a, _b;
|
|
116
|
-
const T = this.types;
|
|
117
|
-
if (path.isBlockStatement()) {
|
|
118
|
-
path.node.body.unshift(T.expressionStatement(increment));
|
|
119
|
-
}
|
|
120
|
-
else if (path.isStatement()) {
|
|
121
|
-
path.insertBefore(T.expressionStatement(increment));
|
|
122
|
-
}
|
|
123
|
-
else if (this.counterNeedsHoisting(path) &&
|
|
124
|
-
T.isVariableDeclarator(path.parent)) {
|
|
125
|
-
// make an attempt to hoist the statement counter, so that
|
|
126
|
-
// function names are maintained.
|
|
127
|
-
const grandParentPath = (_a = path.parentPath) === null || _a === void 0 ? void 0 : _a.parentPath;
|
|
128
|
-
if (grandParentPath && T.isExportNamedDeclaration(grandParentPath.parent)) {
|
|
129
|
-
(_b = grandParentPath.parentPath) === null || _b === void 0 ? void 0 : _b.insertBefore(T.expressionStatement(increment));
|
|
130
|
-
}
|
|
131
|
-
else if (grandParentPath &&
|
|
132
|
-
(T.isProgram(grandParentPath.parent) ||
|
|
133
|
-
T.isBlockStatement(grandParentPath.parent))) {
|
|
134
|
-
grandParentPath.insertBefore(T.expressionStatement(increment));
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
path.replaceWith(T.sequenceExpression([increment, path.node]));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
else if (path.isExpression()) {
|
|
141
|
-
path.replaceWith(T.sequenceExpression([increment, path.node]));
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
console.error('Unable to insert counter for node type:', path.node.type);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
insertFunctionCounter(path) {
|
|
148
|
-
var _a, _b, _c;
|
|
149
|
-
const T = this.types;
|
|
150
|
-
if (!((_a = path.node) === null || _a === void 0 ? void 0 : _a.loc)) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const n = path.node;
|
|
154
|
-
let declarationLocation;
|
|
155
|
-
switch (n.type) {
|
|
156
|
-
case 'FunctionDeclaration':
|
|
157
|
-
case 'FunctionExpression':
|
|
158
|
-
if (n.id) {
|
|
159
|
-
declarationLocation = (_b = n.id.loc) !== null && _b !== void 0 ? _b : undefined;
|
|
160
|
-
}
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
const body = path.get('body');
|
|
164
|
-
const loc = (_c = path.node.loc) !== null && _c !== void 0 ? _c : declarationLocation;
|
|
165
|
-
const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
|
|
166
|
-
if (body.isBlockStatement() && this.shouldInstrument(path, originPos)) {
|
|
167
|
-
// For functions, we only cover the first line of its body.
|
|
168
|
-
originPos.end = originPos.start;
|
|
169
|
-
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
170
|
-
body.node.body.unshift(T.expressionStatement(increment));
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
insertStatementCounter(path) {
|
|
174
|
-
var _a;
|
|
175
|
-
const loc = (_a = path.node) === null || _a === void 0 ? void 0 : _a.loc;
|
|
176
|
-
if (!loc) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
|
|
180
|
-
if (!this.shouldInstrument(path, originPos)) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
184
|
-
this.insertCounter(path, increment);
|
|
185
|
-
}
|
|
186
|
-
insertBranchCounter(path, loc) {
|
|
187
|
-
loc = loc !== null && loc !== void 0 ? loc : path.node.loc;
|
|
188
|
-
if (!loc) {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
|
|
192
|
-
if (this.shouldInstrument(path, originPos)) {
|
|
193
|
-
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
194
|
-
this.insertCounter(path, increment);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
findLeaves(node, accumulator, parent, property) {
|
|
198
|
-
if (!node) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (node.type === 'LogicalExpression') {
|
|
202
|
-
const hint = this.hintFor(node);
|
|
203
|
-
if (hint !== 'next') {
|
|
204
|
-
this.findLeaves(node.left, accumulator, node, 'left');
|
|
205
|
-
this.findLeaves(node.right, accumulator, node, 'right');
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
accumulator.push({
|
|
210
|
-
node,
|
|
211
|
-
parent: parent,
|
|
212
|
-
property: property
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Create a line coverage reporting statement node.
|
|
219
|
-
*/
|
|
220
|
-
function newLineCoverageExpression(originFileId, range) {
|
|
221
|
-
const argumentList = [
|
|
222
|
-
{ type: 'Identifier', name: originFileId },
|
|
223
|
-
{ type: 'NumericLiteral', value: range.start.line }
|
|
224
|
-
];
|
|
225
|
-
// Only pass end line argument if they are different.
|
|
226
|
-
// See also https://v8.dev/blog/adaptor-frame for performance considerations
|
|
227
|
-
if (range.start.line !== range.end.line) {
|
|
228
|
-
argumentList.push({ type: 'NumericLiteral', value: range.end.line });
|
|
229
|
-
}
|
|
230
|
-
return {
|
|
231
|
-
type: 'CallExpression',
|
|
232
|
-
callee: { type: 'Identifier', name: '_$l' },
|
|
233
|
-
arguments: argumentList
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Creates a new string constant AST node.
|
|
238
|
-
*/
|
|
239
|
-
function newStringConstDeclarationNode(name, value) {
|
|
240
|
-
return {
|
|
241
|
-
type: 'VariableDeclaration',
|
|
242
|
-
kind: 'const',
|
|
243
|
-
declarations: [
|
|
244
|
-
{
|
|
245
|
-
type: 'VariableDeclarator',
|
|
246
|
-
id: {
|
|
247
|
-
type: 'Identifier',
|
|
248
|
-
name
|
|
249
|
-
},
|
|
250
|
-
init: {
|
|
251
|
-
type: 'StringLiteral',
|
|
252
|
-
value
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
]
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Generic function that takes a set of visitor methods and
|
|
260
|
-
* returns a visitor object with `enter` and `exit` properties,
|
|
261
|
-
* such that:
|
|
262
|
-
*
|
|
263
|
-
* - standard entry processing is done
|
|
264
|
-
* - the supplied visitors are called only when ignore is not in effect;
|
|
265
|
-
* it reliefs them from worrying about ignore states and generated nodes.
|
|
266
|
-
* - standard exit processing is done
|
|
267
|
-
*/
|
|
268
|
-
function entries(...enter) {
|
|
269
|
-
// the enter function
|
|
270
|
-
const wrappedEntry = function (path, node) {
|
|
271
|
-
this.onEnter(path);
|
|
272
|
-
if (this.shouldIgnore(path)) {
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
enter.forEach(e => {
|
|
276
|
-
e.call(this, path, node);
|
|
277
|
-
});
|
|
278
|
-
};
|
|
279
|
-
const exit = function (path) {
|
|
280
|
-
this.onExit(path);
|
|
281
|
-
};
|
|
282
|
-
return {
|
|
283
|
-
enter: wrappedEntry,
|
|
284
|
-
exit
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
function coverStatement(path) {
|
|
288
|
-
this.insertStatementCounter(path);
|
|
289
|
-
}
|
|
290
|
-
function coverAssignmentPattern(path) {
|
|
291
|
-
this.insertBranchCounter(path.get('right'), undefined);
|
|
292
|
-
}
|
|
293
|
-
function coverFunction(path) {
|
|
294
|
-
this.insertFunctionCounter(path);
|
|
295
|
-
}
|
|
296
|
-
function coverVariableDeclarator(path) {
|
|
297
|
-
this.insertStatementCounter(path.get('init'));
|
|
298
|
-
}
|
|
299
|
-
function coverClassPropDeclarator(path) {
|
|
300
|
-
this.insertStatementCounter(path.get('value'));
|
|
301
|
-
}
|
|
302
|
-
function coverSequenceExpression(path) {
|
|
303
|
-
const T = this.types;
|
|
304
|
-
if (!path.isSequenceExpression()) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
const newExpressions = [];
|
|
308
|
-
for (const expression of path.node.expressions) {
|
|
309
|
-
const [originFileId, originPos] = this.origins.ensureKnownOrigin(expression.loc);
|
|
310
|
-
if (this.shouldInstrument(path, originPos)) {
|
|
311
|
-
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
312
|
-
newExpressions.push(increment);
|
|
313
|
-
}
|
|
314
|
-
// We must add the expression to be evaluated after the coverage increment
|
|
315
|
-
// to not change the return value of the sequence expression.
|
|
316
|
-
newExpressions.push(expression);
|
|
317
|
-
}
|
|
318
|
-
path.replaceWith(T.sequenceExpression(newExpressions));
|
|
319
|
-
}
|
|
320
|
-
function makeBlock(path) {
|
|
321
|
-
const T = this.types;
|
|
322
|
-
if (!path.node) {
|
|
323
|
-
path.replaceWith(T.blockStatement([]));
|
|
324
|
-
}
|
|
325
|
-
if (path.isSequenceExpression()) {
|
|
326
|
-
coverSequenceExpression.call(this, path);
|
|
327
|
-
}
|
|
328
|
-
else if (!path.isBlockStatement()) {
|
|
329
|
-
path.replaceWith(T.blockStatement([path.node]));
|
|
330
|
-
const block = path.node;
|
|
331
|
-
path.node.loc = block.body[0].loc;
|
|
332
|
-
block.body[0].leadingComments = path.node.leadingComments;
|
|
333
|
-
path.node.leadingComments = undefined;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function blockProp(prop) {
|
|
337
|
-
return function (path) {
|
|
338
|
-
makeBlock.call(this, path.get(prop));
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
function makeParenthesizedExpressionForNonIdentifier(path) {
|
|
342
|
-
const T = this.types;
|
|
343
|
-
if (path.node && !path.isIdentifier()) {
|
|
344
|
-
path.replaceWith(T.parenthesizedExpression(path.node));
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
function parenthesizedExpressionProp(prop) {
|
|
348
|
-
return function (path) {
|
|
349
|
-
makeParenthesizedExpressionForNonIdentifier.call(this, path.get(prop));
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
function convertArrowExpression(path) {
|
|
353
|
-
const node = path.node;
|
|
354
|
-
const T = this.types;
|
|
355
|
-
if (!T.isBlockStatement(node.body)) {
|
|
356
|
-
const bloc = node.body.loc;
|
|
357
|
-
if (node.expression === true) {
|
|
358
|
-
node.expression = false;
|
|
359
|
-
}
|
|
360
|
-
node.body = T.blockStatement([T.returnStatement(node.body)]);
|
|
361
|
-
// restore body location
|
|
362
|
-
node.body.loc = bloc;
|
|
363
|
-
// set up the location for the return statement so it gets
|
|
364
|
-
// instrumented
|
|
365
|
-
node.body.body[0].loc = bloc;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function coverIfBranches(path) {
|
|
369
|
-
const n = path.node;
|
|
370
|
-
const hint = this.hintFor(n);
|
|
371
|
-
const ignoreIf = hint === 'if';
|
|
372
|
-
const ignoreElse = hint === 'else';
|
|
373
|
-
if (ignoreIf) {
|
|
374
|
-
this.setAttr(n.consequent, 'skip-all', true);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
this.insertBranchCounter(path.get('consequent'), n.loc);
|
|
378
|
-
}
|
|
379
|
-
if (ignoreElse) {
|
|
380
|
-
this.setAttr(n.alternate, 'skip-all', true);
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
this.insertBranchCounter(path.get('alternate'), undefined);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
function createSwitchBranch(path) {
|
|
387
|
-
// Intentionally left blank
|
|
388
|
-
}
|
|
389
|
-
function coverSwitchCase(path) {
|
|
390
|
-
const T = this.types;
|
|
391
|
-
const loc = path.node.loc;
|
|
392
|
-
if (!loc) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
|
|
396
|
-
if (this.shouldInstrument(path, originPos)) {
|
|
397
|
-
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
398
|
-
path.node.consequent.unshift(T.expressionStatement(increment));
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
function coverTernary(path) {
|
|
402
|
-
const n = path.node;
|
|
403
|
-
const cHint = this.hintFor(n.consequent);
|
|
404
|
-
const aHint = this.hintFor(n.alternate);
|
|
405
|
-
if (cHint !== 'next') {
|
|
406
|
-
this.insertBranchCounter(path.get('consequent'), undefined);
|
|
407
|
-
}
|
|
408
|
-
if (aHint !== 'next') {
|
|
409
|
-
this.insertBranchCounter(path.get('alternate'), undefined);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
function coverLogicalExpression(path) {
|
|
413
|
-
const T = this.types;
|
|
414
|
-
if (path.parentPath.node.type === 'LogicalExpression') {
|
|
415
|
-
return; // already processed
|
|
416
|
-
}
|
|
417
|
-
const leaves = [];
|
|
418
|
-
this.findLeaves(path.node, leaves, undefined, undefined);
|
|
419
|
-
for (const leaf of leaves) {
|
|
420
|
-
const hint = this.hintFor(leaf.node);
|
|
421
|
-
if (hint === 'next') {
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
const loc = path.node.loc;
|
|
425
|
-
if (!loc) {
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
|
|
429
|
-
if (!this.shouldInstrument(path, originPos)) {
|
|
430
|
-
continue;
|
|
431
|
-
}
|
|
432
|
-
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
433
|
-
if (!increment) {
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
leaf.parent[leaf.property] = T.sequenceExpression([
|
|
437
|
-
increment,
|
|
438
|
-
leaf.node
|
|
439
|
-
]);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
const codeVisitor = {
|
|
443
|
-
ArrowFunctionExpression: entries(convertArrowExpression, coverFunction),
|
|
444
|
-
AssignmentPattern: entries(coverAssignmentPattern),
|
|
445
|
-
BlockStatement: entries(), // ignore processing only
|
|
446
|
-
ExportDefaultDeclaration: entries(), // ignore processing only
|
|
447
|
-
ExportNamedDeclaration: entries(), // ignore processing only
|
|
448
|
-
ClassMethod: entries(coverFunction),
|
|
449
|
-
ClassDeclaration: entries(parenthesizedExpressionProp('superClass')),
|
|
450
|
-
ClassProperty: entries(coverClassPropDeclarator),
|
|
451
|
-
ClassPrivateProperty: entries(coverClassPropDeclarator),
|
|
452
|
-
ObjectMethod: entries(coverFunction),
|
|
453
|
-
ExpressionStatement: entries(coverStatement),
|
|
454
|
-
BreakStatement: entries(coverStatement),
|
|
455
|
-
ContinueStatement: entries(coverStatement),
|
|
456
|
-
DebuggerStatement: entries(coverStatement),
|
|
457
|
-
ReturnStatement: entries(coverStatement),
|
|
458
|
-
ThrowStatement: entries(coverStatement),
|
|
459
|
-
TryStatement: entries(coverStatement),
|
|
460
|
-
VariableDeclaration: entries(), // ignore processing only
|
|
461
|
-
VariableDeclarator: entries(coverVariableDeclarator),
|
|
462
|
-
IfStatement: entries(blockProp('consequent'), blockProp('alternate'), coverStatement, coverIfBranches),
|
|
463
|
-
ForStatement: entries(blockProp('body'), coverStatement),
|
|
464
|
-
ForInStatement: entries(blockProp('body'), coverStatement),
|
|
465
|
-
ForOfStatement: entries(blockProp('body'), coverStatement),
|
|
466
|
-
WhileStatement: entries(blockProp('body'), coverStatement),
|
|
467
|
-
DoWhileStatement: entries(blockProp('body'), coverStatement),
|
|
468
|
-
SwitchStatement: entries(createSwitchBranch, coverStatement),
|
|
469
|
-
SwitchCase: entries(coverSwitchCase),
|
|
470
|
-
WithStatement: entries(blockProp('body'), coverStatement),
|
|
471
|
-
FunctionDeclaration: entries(coverFunction),
|
|
472
|
-
FunctionExpression: entries(coverFunction),
|
|
473
|
-
LabeledStatement: entries(coverStatement),
|
|
474
|
-
ConditionalExpression: entries(coverTernary),
|
|
475
|
-
LogicalExpression: entries(coverLogicalExpression),
|
|
476
|
-
SequenceExpression: entries(coverSequenceExpression),
|
|
477
|
-
};
|
|
478
|
-
/**
|
|
479
|
-
* The rewire plugin (and potentially other babel middleware)
|
|
480
|
-
* may cause files to be instrumented twice, see:
|
|
481
|
-
* https://github.com/istanbuljs/babel-plugin-istanbul/issues/94
|
|
482
|
-
* we should only instrument code for coverage the first time
|
|
483
|
-
* it's run through lib-instrument.
|
|
484
|
-
*/
|
|
485
|
-
function alreadyInstrumented(path, visitState) {
|
|
486
|
-
return path.scope.hasBinding(visitState.varName);
|
|
487
|
-
}
|
|
488
|
-
function getParentComments(path) {
|
|
489
|
-
if (!(path === null || path === void 0 ? void 0 : path.parent)) {
|
|
490
|
-
return [];
|
|
491
|
-
}
|
|
492
|
-
if (!('comments' in path.parent)) {
|
|
493
|
-
return [];
|
|
494
|
-
}
|
|
495
|
-
return path.parent.comments;
|
|
496
|
-
}
|
|
497
|
-
function shouldIgnoreFile(programNodePath) {
|
|
498
|
-
if (!programNodePath) {
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
502
|
-
return getParentComments(programNodePath).some(c => COMMENT_FILE_RE.test(c.value));
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* `programVisitor` is a `babel` adaptor for instrumentation.
|
|
506
|
-
*
|
|
507
|
-
* It returns an object with two methods `enter` and `exit`.
|
|
508
|
-
* These should be assigned to or called from `Program` entry and exit functions
|
|
509
|
-
* in a babel visitor.
|
|
510
|
-
*
|
|
511
|
-
* These functions do not make assumptions about the state set by Babel and thus
|
|
512
|
-
* can be used in a context other than a Babel plugin.
|
|
513
|
-
*
|
|
514
|
-
* The exit function returns an object that currently has the following keys:
|
|
515
|
-
*
|
|
516
|
-
* `fileCoverage` - the file coverage object created for the source file.
|
|
517
|
-
* `sourceMappingURL` - any source mapping URL found when processing the file.
|
|
518
|
-
*
|
|
519
|
-
* @param types - an instance of babel-types.
|
|
520
|
-
* @param inputSourceMapConsumer - access object for the source map of the input.
|
|
521
|
-
* @param opts - additional options.
|
|
522
|
-
*/
|
|
523
|
-
function programVisitor(types, inputSourceMapConsumer, opts) {
|
|
524
|
-
opts = { ...opts };
|
|
525
|
-
const visitState = new VisitState(types, inputSourceMapConsumer, opts.ignoreClassMethods, opts.reportLogic, opts.shouldInstrumentCallback);
|
|
526
|
-
return {
|
|
527
|
-
enter(path) {
|
|
528
|
-
if (shouldIgnoreFile(path.find(p => p.isProgram()))) {
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
if (alreadyInstrumented(path, visitState)) {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
path.traverse(codeVisitor, visitState);
|
|
535
|
-
},
|
|
536
|
-
exit(path) {
|
|
537
|
-
if (alreadyInstrumented(path, visitState)) {
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
const originData = visitState.origins;
|
|
541
|
-
if (shouldIgnoreFile(path.find(p => p.isProgram()))) {
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
const body = path.node.body;
|
|
545
|
-
if (opts.codeToPrepend) {
|
|
546
|
-
const codeToPrependAst = (0, core_1.parse)(opts.codeToPrepend, { sourceType: 'script' });
|
|
547
|
-
if (codeToPrependAst !== null) {
|
|
548
|
-
body.unshift(...codeToPrependAst.program.body);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
// Add a variable definition for each origin file on top of the file.
|
|
552
|
-
for (const [originPath, originId] of originData.originToIdMap.entries()) {
|
|
553
|
-
const declaration = newStringConstDeclarationNode(originId, originPath);
|
|
554
|
-
body.unshift(declaration);
|
|
555
|
-
}
|
|
556
|
-
// Add a token for signaling that the file has been instrumented.
|
|
557
|
-
if (opts.isInstrumentedToken) {
|
|
558
|
-
types.addComment(path.node, 'leading', opts.isInstrumentedToken, false);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
}
|