@teamscale/lib-instrument 1.0.0-beta.7 → 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/visitor.js DELETED
@@ -1,481 +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
- const COMMENT_RE = /^\s*istanbul\s+ignore\s+(if|else|next)(?=\W|$)/;
7
- const COMMENT_FILE_RE = /^\s*istanbul\s+ignore\s+(file)(?=\W|$)/;
8
- class VisitState {
9
- types;
10
- attrs;
11
- nextIgnore;
12
- ignoreClassMethods;
13
- reportLogic;
14
- shouldInstrumentCallback;
15
- origins;
16
- constructor(types, inputSourceMapConsumer, ignoreClassMethods = [], reportLogic = false, shouldInstrumentCallback) {
17
- this.attrs = {};
18
- this.nextIgnore = null;
19
- this.ignoreClassMethods = ignoreClassMethods;
20
- this.types = types;
21
- this.reportLogic = reportLogic;
22
- this.shouldInstrumentCallback = shouldInstrumentCallback;
23
- this.origins = new origins_1.SourceOrigins(inputSourceMapConsumer);
24
- }
25
- shouldInstrument(path, loc) {
26
- if (this.shouldInstrumentCallback) {
27
- return this.shouldInstrumentCallback(path, loc);
28
- }
29
- return true;
30
- }
31
- shouldIgnore(path) {
32
- return this.nextIgnore !== null || !path.node.loc;
33
- }
34
- hintFor(node) {
35
- let hint = null;
36
- if (node.leadingComments) {
37
- node.leadingComments.forEach(c => {
38
- const v = (c.value || '').trim();
39
- const groups = COMMENT_RE.exec(v);
40
- if (groups) {
41
- hint = groups[1];
42
- }
43
- });
44
- }
45
- return hint;
46
- }
47
- counterNeedsHoisting(path) {
48
- return (path.isFunctionExpression() ||
49
- path.isArrowFunctionExpression() ||
50
- path.isClassExpression());
51
- }
52
- onEnter(path) {
53
- const n = path.node;
54
- if (this.nextIgnore !== null) {
55
- return;
56
- }
57
- const hint = this.hintFor(n);
58
- if (hint === 'next') {
59
- this.nextIgnore = n;
60
- return;
61
- }
62
- if (this.getAttr(path.node, 'skip-all') !== null) {
63
- this.nextIgnore = n;
64
- }
65
- if (path.isFunctionExpression() &&
66
- this.ignoreClassMethods.some(name => name === path.node.id?.name)) {
67
- this.nextIgnore = n;
68
- return;
69
- }
70
- if (path.isClassMethod() &&
71
- this.ignoreClassMethods.some(name => name === path.node.key.name)) {
72
- this.nextIgnore = n;
73
- return;
74
- }
75
- }
76
- onExit(path) {
77
- if (path.node === this.nextIgnore) {
78
- this.nextIgnore = null;
79
- }
80
- delete path.node.__cov__;
81
- }
82
- setAttr(node, name, value) {
83
- node.__cov__ = node.__cov__ || {};
84
- node.__cov__[name] = value;
85
- }
86
- getAttr(node, name) {
87
- const c = node.__cov__;
88
- if (!c) {
89
- return null;
90
- }
91
- return c[name];
92
- }
93
- insertCounter(path, increment) {
94
- const T = this.types;
95
- if (path.isBlockStatement()) {
96
- path.node.body.unshift(T.expressionStatement(increment));
97
- }
98
- else if (path.isStatement()) {
99
- path.insertBefore(T.expressionStatement(increment));
100
- }
101
- else if (this.counterNeedsHoisting(path) &&
102
- T.isVariableDeclarator(path.parent)) {
103
- const grandParentPath = path.parentPath?.parentPath;
104
- if (grandParentPath && T.isExportNamedDeclaration(grandParentPath.parent)) {
105
- grandParentPath.parentPath?.insertBefore(T.expressionStatement(increment));
106
- }
107
- else if (grandParentPath &&
108
- (T.isProgram(grandParentPath.parent) ||
109
- T.isBlockStatement(grandParentPath.parent))) {
110
- grandParentPath.insertBefore(T.expressionStatement(increment));
111
- }
112
- else {
113
- path.replaceWith(T.sequenceExpression([increment, path.node]));
114
- }
115
- }
116
- else if (path.isExpression()) {
117
- path.replaceWith(T.sequenceExpression([increment, path.node]));
118
- }
119
- else {
120
- console.error('Unable to insert counter for node type:', path.node.type);
121
- }
122
- }
123
- insertFunctionCounter(path) {
124
- const T = this.types;
125
- if (!(path.node?.loc)) {
126
- return;
127
- }
128
- const n = path.node;
129
- let declarationLocation;
130
- switch (n.type) {
131
- case 'FunctionDeclaration':
132
- case 'FunctionExpression':
133
- if (n.id) {
134
- declarationLocation = n.id.loc ?? undefined;
135
- }
136
- break;
137
- }
138
- const body = path.get('body');
139
- const loc = path.node.loc ?? declarationLocation;
140
- const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
141
- if (body.isBlockStatement() && this.shouldInstrument(path, originPos)) {
142
- originPos.end = originPos.start;
143
- const increment = newLineCoverageExpression(originFileId, originPos);
144
- body.node.body.unshift(T.expressionStatement(increment));
145
- }
146
- }
147
- insertStatementCounter(path) {
148
- const loc = path.node?.loc;
149
- if (!loc) {
150
- return;
151
- }
152
- const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
153
- if (!this.shouldInstrument(path, originPos)) {
154
- return;
155
- }
156
- const increment = newLineCoverageExpression(originFileId, originPos);
157
- this.insertCounter(path, increment);
158
- }
159
- insertBranchCounter(path, loc) {
160
- loc = loc ?? path.node.loc;
161
- if (!loc) {
162
- return;
163
- }
164
- const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
165
- if (this.shouldInstrument(path, originPos)) {
166
- const increment = newLineCoverageExpression(originFileId, originPos);
167
- this.insertCounter(path, increment);
168
- }
169
- }
170
- findLeaves(node, accumulator, parent, property) {
171
- if (!node) {
172
- return;
173
- }
174
- if (node.type === 'LogicalExpression') {
175
- const hint = this.hintFor(node);
176
- if (hint !== 'next') {
177
- this.findLeaves(node.left, accumulator, node, 'left');
178
- this.findLeaves(node.right, accumulator, node, 'right');
179
- }
180
- }
181
- else {
182
- accumulator.push({
183
- node,
184
- parent: parent,
185
- property: property
186
- });
187
- }
188
- }
189
- }
190
- function newLineCoverageExpression(originFileId, range) {
191
- const argumentList = [
192
- { type: 'Identifier', name: originFileId },
193
- { type: 'NumericLiteral', value: range.start.line }
194
- ];
195
- if (range.start.line !== range.end.line) {
196
- argumentList.push({ type: 'NumericLiteral', value: range.end.line });
197
- }
198
- return {
199
- type: 'CallExpression',
200
- callee: { type: 'Identifier', name: '_$l' },
201
- arguments: argumentList
202
- };
203
- }
204
- function newStringConstDeclarationNode(name, value) {
205
- return {
206
- type: 'VariableDeclaration',
207
- kind: 'const',
208
- declarations: [
209
- {
210
- type: 'VariableDeclarator',
211
- id: {
212
- type: 'Identifier',
213
- name
214
- },
215
- init: {
216
- type: 'StringLiteral',
217
- value
218
- }
219
- }
220
- ]
221
- };
222
- }
223
- function entries(...enter) {
224
- const wrappedEntry = function (path, node) {
225
- this.onEnter(path);
226
- if (this.shouldIgnore(path)) {
227
- return;
228
- }
229
- enter.forEach(e => {
230
- e.call(this, path, node);
231
- });
232
- };
233
- const exit = function (path) {
234
- this.onExit(path);
235
- };
236
- return {
237
- enter: wrappedEntry,
238
- exit
239
- };
240
- }
241
- function coverStatement(path) {
242
- this.insertStatementCounter(path);
243
- }
244
- function coverAssignmentPattern(path) {
245
- this.insertBranchCounter(path.get('right'), undefined);
246
- }
247
- function coverFunction(path) {
248
- this.insertFunctionCounter(path);
249
- }
250
- function coverVariableDeclarator(path) {
251
- this.insertStatementCounter(path.get('init'));
252
- }
253
- function coverClassPropDeclarator(path) {
254
- this.insertStatementCounter(path.get('value'));
255
- }
256
- function coverSequenceExpression(path) {
257
- const T = this.types;
258
- if (!path.isSequenceExpression()) {
259
- return;
260
- }
261
- const newExpressions = [];
262
- for (const expression of path.node.expressions) {
263
- const [originFileId, originPos] = this.origins.ensureKnownOrigin(expression.loc);
264
- if (this.shouldInstrument(path, originPos)) {
265
- const increment = newLineCoverageExpression(originFileId, originPos);
266
- newExpressions.push(increment);
267
- }
268
- newExpressions.push(expression);
269
- }
270
- path.replaceWith(T.sequenceExpression(newExpressions));
271
- }
272
- function makeBlock(path) {
273
- const T = this.types;
274
- if (!path.node) {
275
- path.replaceWith(T.blockStatement([]));
276
- }
277
- if (path.isSequenceExpression()) {
278
- coverSequenceExpression.call(this, path);
279
- }
280
- else if (!path.isBlockStatement()) {
281
- path.replaceWith(T.blockStatement([path.node]));
282
- const block = path.node;
283
- path.node.loc = block.body[0].loc;
284
- block.body[0].leadingComments = path.node.leadingComments;
285
- path.node.leadingComments = undefined;
286
- }
287
- }
288
- function blockProp(prop) {
289
- return function (path) {
290
- makeBlock.call(this, path.get(prop));
291
- };
292
- }
293
- function makeParenthesizedExpressionForNonIdentifier(path) {
294
- const T = this.types;
295
- if (path.node && !path.isIdentifier()) {
296
- path.replaceWith(T.parenthesizedExpression(path.node));
297
- }
298
- }
299
- function parenthesizedExpressionProp(prop) {
300
- return function (path) {
301
- makeParenthesizedExpressionForNonIdentifier.call(this, path.get(prop));
302
- };
303
- }
304
- function convertArrowExpression(path) {
305
- const node = path.node;
306
- const T = this.types;
307
- if (!T.isBlockStatement(node.body)) {
308
- const bloc = node.body.loc;
309
- if (node.expression) {
310
- node.expression = false;
311
- }
312
- node.body = T.blockStatement([T.returnStatement(node.body)]);
313
- node.body.loc = bloc;
314
- node.body.body[0].loc = bloc;
315
- }
316
- }
317
- function coverIfBranches(path) {
318
- const n = path.node;
319
- const hint = this.hintFor(n);
320
- const ignoreIf = hint === 'if';
321
- const ignoreElse = hint === 'else';
322
- if (ignoreIf) {
323
- this.setAttr(n.consequent, 'skip-all', true);
324
- }
325
- else {
326
- this.insertBranchCounter(path.get('consequent'), n.loc);
327
- }
328
- if (ignoreElse) {
329
- this.setAttr(n.alternate, 'skip-all', true);
330
- }
331
- else {
332
- this.insertBranchCounter(path.get('alternate'), undefined);
333
- }
334
- }
335
- function createSwitchBranch() {
336
- }
337
- function coverSwitchCase(path) {
338
- const T = this.types;
339
- const loc = path.node.loc;
340
- if (!loc) {
341
- return;
342
- }
343
- const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
344
- if (this.shouldInstrument(path, originPos)) {
345
- const increment = newLineCoverageExpression(originFileId, originPos);
346
- path.node.consequent.unshift(T.expressionStatement(increment));
347
- }
348
- }
349
- function coverTernary(path) {
350
- const n = path.node;
351
- const cHint = this.hintFor(n.consequent);
352
- const aHint = this.hintFor(n.alternate);
353
- if (cHint !== 'next') {
354
- this.insertBranchCounter(path.get('consequent'), undefined);
355
- }
356
- if (aHint !== 'next') {
357
- this.insertBranchCounter(path.get('alternate'), undefined);
358
- }
359
- }
360
- function coverLogicalExpression(path) {
361
- const T = this.types;
362
- if (path.parentPath.node.type === 'LogicalExpression') {
363
- return;
364
- }
365
- const leaves = [];
366
- this.findLeaves(path.node, leaves, undefined, undefined);
367
- for (const leaf of leaves) {
368
- const hint = this.hintFor(leaf.node);
369
- if (hint === 'next') {
370
- continue;
371
- }
372
- const loc = path.node.loc;
373
- if (!loc) {
374
- continue;
375
- }
376
- const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
377
- if (!this.shouldInstrument(path, originPos)) {
378
- continue;
379
- }
380
- const increment = newLineCoverageExpression(originFileId, originPos);
381
- if (!increment) {
382
- continue;
383
- }
384
- leaf.parent[leaf.property] = T.sequenceExpression([
385
- increment,
386
- leaf.node
387
- ]);
388
- }
389
- }
390
- const codeVisitor = {
391
- ArrowFunctionExpression: entries(convertArrowExpression, coverFunction),
392
- AssignmentPattern: entries(coverAssignmentPattern),
393
- BlockStatement: entries(),
394
- ExportDefaultDeclaration: entries(),
395
- ExportNamedDeclaration: entries(),
396
- ClassMethod: entries(coverFunction),
397
- ClassDeclaration: entries(parenthesizedExpressionProp('superClass')),
398
- ClassProperty: entries(coverClassPropDeclarator),
399
- ClassPrivateProperty: entries(coverClassPropDeclarator),
400
- ObjectMethod: entries(coverFunction),
401
- ExpressionStatement: entries(coverStatement),
402
- BreakStatement: entries(coverStatement),
403
- ContinueStatement: entries(coverStatement),
404
- DebuggerStatement: entries(coverStatement),
405
- ReturnStatement: entries(coverStatement),
406
- ThrowStatement: entries(coverStatement),
407
- TryStatement: entries(coverStatement),
408
- VariableDeclaration: entries(),
409
- VariableDeclarator: entries(coverVariableDeclarator),
410
- IfStatement: entries(blockProp('consequent'), blockProp('alternate'), coverStatement, coverIfBranches),
411
- ForStatement: entries(blockProp('body'), coverStatement),
412
- ForInStatement: entries(blockProp('body'), coverStatement),
413
- ForOfStatement: entries(blockProp('body'), coverStatement),
414
- WhileStatement: entries(blockProp('body'), coverStatement),
415
- DoWhileStatement: entries(blockProp('body'), coverStatement),
416
- SwitchStatement: entries(createSwitchBranch, coverStatement),
417
- SwitchCase: entries(coverSwitchCase),
418
- WithStatement: entries(blockProp('body'), coverStatement),
419
- FunctionDeclaration: entries(coverFunction),
420
- FunctionExpression: entries(coverFunction),
421
- LabeledStatement: entries(coverStatement),
422
- ConditionalExpression: entries(coverTernary),
423
- LogicalExpression: entries(coverLogicalExpression),
424
- SequenceExpression: entries(coverSequenceExpression),
425
- };
426
- function alreadyInstrumented(path, visitState) {
427
- return path.scope.hasBinding(visitState.varName);
428
- }
429
- function getParentComments(path) {
430
- if (!path?.parent) {
431
- return [];
432
- }
433
- if (!('comments' in path.parent)) {
434
- return [];
435
- }
436
- return path.parent.comments;
437
- }
438
- function shouldIgnoreFile(programNodePath) {
439
- if (!programNodePath) {
440
- return false;
441
- }
442
- return getParentComments(programNodePath).some(c => COMMENT_FILE_RE.test(c.value));
443
- }
444
- function programVisitor(types, inputSourceMapConsumer, opts) {
445
- opts = { ...opts };
446
- const visitState = new VisitState(types, inputSourceMapConsumer, opts.ignoreClassMethods, opts.reportLogic, opts.shouldInstrumentCallback);
447
- return {
448
- enter(path) {
449
- if (shouldIgnoreFile(path.find(p => p.isProgram()))) {
450
- return;
451
- }
452
- if (alreadyInstrumented(path, visitState)) {
453
- return;
454
- }
455
- path.traverse(codeVisitor, visitState);
456
- },
457
- exit(path) {
458
- if (alreadyInstrumented(path, visitState)) {
459
- return;
460
- }
461
- const originData = visitState.origins;
462
- if (shouldIgnoreFile(path.find(p => p.isProgram()))) {
463
- return;
464
- }
465
- const body = path.node.body;
466
- if (opts.codeToPrepend) {
467
- const codeToPrependAst = (0, core_1.parse)(opts.codeToPrepend, { sourceType: 'script' });
468
- if (codeToPrependAst !== null) {
469
- body.unshift(...codeToPrependAst.program.body);
470
- }
471
- }
472
- for (const [originPath, originId] of originData.originToIdMap.entries()) {
473
- const declaration = newStringConstDeclarationNode(originId, originPath);
474
- body.unshift(declaration);
475
- }
476
- if (opts.isInstrumentedToken) {
477
- types.addComment(path.node, 'leading', opts.isInstrumentedToken, false);
478
- }
479
- }
480
- };
481
- }