@teamscale/javascript-instrumenter 0.0.1-beta.41 → 0.0.1-beta.42
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 +0 -24
- package/dist/package.json +3 -4
- package/dist/src/instrumenter/Instrumenter.d.ts.map +1 -1
- package/dist/src/instrumenter/Instrumenter.js +3 -3
- package/dist/src/instrumenter/{Cleaner.d.ts → Postprocessor.d.ts} +1 -1
- package/dist/src/instrumenter/Postprocessor.d.ts.map +1 -0
- package/dist/src/instrumenter/Postprocessor.js +254 -0
- package/dist/vaccine.js +1 -1
- package/package.json +3 -4
- package/dist/src/instrumenter/Cleaner.d.ts.map +0 -1
- package/dist/src/instrumenter/Cleaner.js +0 -87
package/README.md
CHANGED
|
@@ -39,30 +39,6 @@ or via `npx` by running
|
|
|
39
39
|
npx @teamscale/javascript-instrumenter
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
### Instrumenting a Single File
|
|
43
|
-
|
|
44
|
-
Adds information for coverage tracking to a single file, either
|
|
45
|
-
in-place (by replacing the previous content), or by producing a new file.
|
|
46
|
-
|
|
47
|
-
An in-place transformation is only allowed if the given file is already
|
|
48
|
-
the result of an automatic transformation process, that is, if a source-map is available.
|
|
49
|
-
|
|
50
|
-
If the source-map is not provided as an explicit argument, either
|
|
51
|
-
the file must contain source-map information, or the source-map file
|
|
52
|
-
must be placed along with the source file in the same directory.
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
yarn instrumenter --inplace ./the/path/to/the/file.js
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
yarn instrumenter --inplace ./the/path/to/the/file.js --source-map ./the/path/to/the/source.map
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
yarn instrumenter ./the/path/to/the/file.js --to ./the/file/path/to/write/to.js
|
|
64
|
-
```
|
|
65
|
-
|
|
66
42
|
## Limitations
|
|
67
43
|
|
|
68
44
|
This tool inherits most of the limitations of IstanbulJs, including
|
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamscale/javascript-instrumenter",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.1-beta.42",
|
|
4
|
+
"description": "JavaScript coverage instrumenter with coverage forwarding to a collector process",
|
|
5
5
|
"main": "dist/src/main.js",
|
|
6
6
|
"bin": "dist/src/main.js",
|
|
7
7
|
"types": "dist/src/main.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@babel/generator": "^7.18.2",
|
|
51
51
|
"@babel/parser": "^7.18.5",
|
|
52
|
-
"@babel/traverse": "^7.18.
|
|
52
|
+
"@babel/traverse": "^7.18.6",
|
|
53
53
|
"@babel/types": "^7.18.4",
|
|
54
54
|
"@cqse/commons": "^0.0.1-beta.1",
|
|
55
55
|
"@types/micromatch": "^4.0.2",
|
|
@@ -62,7 +62,6 @@
|
|
|
62
62
|
"istanbul-lib-instrument": "^5.2.0",
|
|
63
63
|
"micromatch": "4.0.4",
|
|
64
64
|
"mkdirp": "^1.0.4",
|
|
65
|
-
"nyc": "^15.1.0",
|
|
66
65
|
"source-map": "0.7.3",
|
|
67
66
|
"typescript-optional": "^2.0.1",
|
|
68
67
|
"unload": "^2.2.0",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Instrumenter.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Instrumenter.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAY,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAOvE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,qBAAqB,8CAA8C,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC3D;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAS;gBAEX,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IASnD;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBhE;;;;;;;OAOG;IACG,aAAa,CAClB,SAAS,EAAE,kBAAkB,EAC7B,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,mBAAmB,EAClC,eAAe,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"Instrumenter.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Instrumenter.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAY,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAOvE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,qBAAqB,8CAA8C,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC3D;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAS;gBAEX,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IASnD;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBhE;;;;;;;OAOG;IACG,aAAa,CAClB,SAAS,EAAE,kBAAkB,EAC7B,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,mBAAmB,EAClC,eAAe,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAAC,UAAU,CAAC;YAwGR,6BAA6B;IA8C3C;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAcpC,0GAA0G;IAC1G,OAAO,CAAC,WAAW;IASnB,uHAAuH;IACvH,OAAO,CAAC,4BAA4B;CASpC;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAClC,kBAAkB,EAAE,MAAM,EAC1B,0BAA0B,EAAE,MAAM,GAChC,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAUxC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,qBAAqB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GACjD,YAAY,GAAG,SAAS,CAU1B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAsC7G;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAGlF"}
|
|
@@ -35,7 +35,7 @@ const fs = __importStar(require("fs"));
|
|
|
35
35
|
const mkdirp = __importStar(require("mkdirp"));
|
|
36
36
|
const path = __importStar(require("path"));
|
|
37
37
|
const convertSourceMap = __importStar(require("convert-source-map"));
|
|
38
|
-
const
|
|
38
|
+
const Postprocessor_1 = require("./Postprocessor");
|
|
39
39
|
const typescript_optional_1 = require("typescript-optional");
|
|
40
40
|
const async_1 = __importDefault(require("async"));
|
|
41
41
|
exports.IS_INSTRUMENTED_TOKEN = '/** $IS_JS_PROFILER_INSTRUMENTED=true **/';
|
|
@@ -116,7 +116,7 @@ class IstanbulInstrumenter {
|
|
|
116
116
|
const instrumentedSourcemap = instrumenter.lastSourceMap();
|
|
117
117
|
let instrumentedAndCleanedSource = await this.removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern, instrumentedSourcemap);
|
|
118
118
|
instrumentedAndCleanedSource = instrumentedAndCleanedSource
|
|
119
|
-
.replace(/actualCoverage\s*=\s*coverage\[path\]/g, 'actualCoverage=
|
|
119
|
+
.replace(/actualCoverage\s*=\s*coverage\[path\]/g, 'actualCoverage=_$registerCoverageObject(coverage[path])')
|
|
120
120
|
.replace(/new Function\("return this"\)\(\)/g, "typeof window === 'object' ? window : this");
|
|
121
121
|
// The process also can result in a new source map that we will append in the result.
|
|
122
122
|
//
|
|
@@ -149,7 +149,7 @@ class IstanbulInstrumenter {
|
|
|
149
149
|
}
|
|
150
150
|
const removedInstrumentationFor = new Set();
|
|
151
151
|
// Remove the unwanted instrumentation
|
|
152
|
-
const cleaned = (0,
|
|
152
|
+
const cleaned = (0, Postprocessor_1.cleanSourceCode)(instrumentedSource, configurationAlternative.esModules, location => {
|
|
153
153
|
const originalPosition = instrumentedSourceMapConsumer.originalPositionFor({
|
|
154
154
|
line: location.start.line,
|
|
155
155
|
column: location.start.column
|
|
@@ -6,4 +6,4 @@ import { SourceLocation } from '@babel/types';
|
|
|
6
6
|
* An instrumentation is removed if the hook `makeCoverable` returns `false`.
|
|
7
7
|
*/
|
|
8
8
|
export declare function cleanSourceCode(code: string, esModules: boolean, makeCoverable: (location: SourceLocation) => boolean): string;
|
|
9
|
-
//# sourceMappingURL=
|
|
9
|
+
//# sourceMappingURL=Postprocessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Postprocessor.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Postprocessor.ts"],"names":[],"mappings":"AAGA,OAAO,EAQN,cAAc,EAWd,MAAM,cAAc,CAAC;AA0ItB;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,GAClD,MAAM,CAaR"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.cleanSourceCode = void 0;
|
|
7
|
+
const parser_1 = require("@babel/parser");
|
|
8
|
+
const generator_1 = __importDefault(require("@babel/generator"));
|
|
9
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
10
|
+
const types_1 = require("@babel/types");
|
|
11
|
+
const commons_1 = require("@cqse/commons");
|
|
12
|
+
const COVERAGE_OBJ_FUNCTION_NAME_PREFIX = 'cov_';
|
|
13
|
+
function getIstanbulCoverageFunctionDeclarationName(node) {
|
|
14
|
+
var _a;
|
|
15
|
+
if (!(0, types_1.isFunctionDeclaration)(node)) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const functionName = (_a = node.id) === null || _a === void 0 ? void 0 : _a.name;
|
|
19
|
+
if (functionName === null || functionName === void 0 ? void 0 : functionName.startsWith(COVERAGE_OBJ_FUNCTION_NAME_PREFIX)) {
|
|
20
|
+
return functionName;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function createFileIdMappingHandler() {
|
|
27
|
+
const fileIdMap = new Map();
|
|
28
|
+
let fileIdSeq = 0;
|
|
29
|
+
return {
|
|
30
|
+
enterPath(path) {
|
|
31
|
+
var _a;
|
|
32
|
+
if (!(0, types_1.isVariableDeclaration)(path.node)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const grandParentPath = (_a = path.parentPath) === null || _a === void 0 ? void 0 : _a.parentPath;
|
|
36
|
+
const coverageFunctionName = getIstanbulCoverageFunctionDeclarationName(grandParentPath === null || grandParentPath === void 0 ? void 0 : grandParentPath.node);
|
|
37
|
+
if (grandParentPath && coverageFunctionName) {
|
|
38
|
+
const declaration = path.node;
|
|
39
|
+
if (declaration.declarations.length === 1) {
|
|
40
|
+
const declarator = declaration.declarations[0];
|
|
41
|
+
if ((0, types_1.isIdentifier)(declarator.id) && declarator.id.name === 'hash') {
|
|
42
|
+
// We take note of the hash that is stored within the `cov_*' function.
|
|
43
|
+
const fileIdVarName = `_$fid${fileIdSeq++}`;
|
|
44
|
+
const fileId = declarator.init.value;
|
|
45
|
+
fileIdMap.set(coverageFunctionName, fileIdVarName);
|
|
46
|
+
grandParentPath.insertBefore(newStringConstDeclarationNode(fileIdVarName, fileId));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
getFileHashForCoverageObjectId(coverageObjectId) {
|
|
52
|
+
return fileIdMap.get(coverageObjectId);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function createPartialInstrumentationHandler(fileIdMappingHandler) {
|
|
57
|
+
return {
|
|
58
|
+
enterPath(path, makeCoverable) {
|
|
59
|
+
if (!(0, types_1.isUpdateExpression)(path.node)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const increment = extractCoverageIncrement(path.node);
|
|
63
|
+
if (!increment) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const wantCoverageIncrement = path.node.loc && makeCoverable(path.node.loc) && increment.type !== 'function';
|
|
67
|
+
// Add a new coverage instrument if desired
|
|
68
|
+
if (wantCoverageIncrement) {
|
|
69
|
+
const fileIdVarName = fileIdMappingHandler.getFileHashForCoverageObjectId(increment.coverageObjectId);
|
|
70
|
+
if (!fileIdVarName) {
|
|
71
|
+
throw new commons_1.IllegalStateException(`File ID variable for coverage object with ID ${increment.coverageObjectId} not found!`);
|
|
72
|
+
}
|
|
73
|
+
const insertAsExpression = (0, types_1.isSequenceExpression)(path.parent);
|
|
74
|
+
insertNodeBefore(path, newCoverageIncrementNode(fileIdVarName, increment, insertAsExpression));
|
|
75
|
+
}
|
|
76
|
+
// Remove the existing coverage increment node
|
|
77
|
+
path.remove();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Remove IstanbulJs instrumentations based on the given
|
|
83
|
+
* hook `makeCoverable`.
|
|
84
|
+
*
|
|
85
|
+
* An instrumentation is removed if the hook `makeCoverable` returns `false`.
|
|
86
|
+
*/
|
|
87
|
+
function cleanSourceCode(code, esModules, makeCoverable) {
|
|
88
|
+
const ast = (0, parser_1.parse)(code, { sourceType: esModules ? 'module' : 'script' });
|
|
89
|
+
const fileIdMappingHandler = createFileIdMappingHandler();
|
|
90
|
+
const partialInstrumentationHandler = createPartialInstrumentationHandler(fileIdMappingHandler);
|
|
91
|
+
(0, traverse_1.default)(ast, {
|
|
92
|
+
enter(path) {
|
|
93
|
+
fileIdMappingHandler.enterPath(path);
|
|
94
|
+
partialInstrumentationHandler.enterPath(path, makeCoverable);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return (0, generator_1.default)(ast, {}, code).code;
|
|
98
|
+
}
|
|
99
|
+
exports.cleanSourceCode = cleanSourceCode;
|
|
100
|
+
/**
|
|
101
|
+
* We cannot just run `path.insertBefore` to add a new element to an AST.
|
|
102
|
+
* See https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#toc-inserting-a-sibling-node .
|
|
103
|
+
*
|
|
104
|
+
* Special handling for some container nodes is needed.
|
|
105
|
+
*/
|
|
106
|
+
function insertNodeBefore(path, toInsert) {
|
|
107
|
+
if ((0, types_1.isSequenceExpression)(path.parent)) {
|
|
108
|
+
path.parentPath.unshiftContainer('expressions', [toInsert]);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
path.insertBefore(toInsert);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Creates a new string constant AST node.
|
|
116
|
+
*/
|
|
117
|
+
function newStringConstDeclarationNode(name, value) {
|
|
118
|
+
return {
|
|
119
|
+
type: 'VariableDeclaration',
|
|
120
|
+
kind: 'const',
|
|
121
|
+
declarations: [
|
|
122
|
+
{
|
|
123
|
+
type: 'VariableDeclarator',
|
|
124
|
+
id: {
|
|
125
|
+
type: 'Identifier',
|
|
126
|
+
name
|
|
127
|
+
},
|
|
128
|
+
init: {
|
|
129
|
+
type: 'StringLiteral',
|
|
130
|
+
value
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Creates a new coverage increment statement.
|
|
138
|
+
*/
|
|
139
|
+
function newCoverageIncrementNode(fileIdVarName, increment, asExpression) {
|
|
140
|
+
let expression;
|
|
141
|
+
if (increment.type === 'branch') {
|
|
142
|
+
expression = newBranchCoverageIncrementExpression(fileIdVarName, increment);
|
|
143
|
+
}
|
|
144
|
+
else if (increment.type === 'statement') {
|
|
145
|
+
expression = newStatementCoverageIncrementExpression(fileIdVarName, increment);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
throw new Error(`Unexpected coverage increment type: ${increment.type}`);
|
|
149
|
+
}
|
|
150
|
+
if (asExpression) {
|
|
151
|
+
return expression;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
type: 'ExpressionStatement',
|
|
155
|
+
expression
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a branch coverage increment node.
|
|
160
|
+
*/
|
|
161
|
+
function newBranchCoverageIncrementExpression(fileIdVarName, increment) {
|
|
162
|
+
return {
|
|
163
|
+
type: 'CallExpression',
|
|
164
|
+
callee: { type: 'Identifier', name: '_$brCov' },
|
|
165
|
+
arguments: [
|
|
166
|
+
{ type: 'Identifier', name: fileIdVarName },
|
|
167
|
+
{ type: 'NumericLiteral', value: increment.branchId },
|
|
168
|
+
{ type: 'NumericLiteral', value: increment.locationId }
|
|
169
|
+
]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Create a statement coverage increment node.
|
|
174
|
+
*/
|
|
175
|
+
function newStatementCoverageIncrementExpression(fileIdVarName, increment) {
|
|
176
|
+
return {
|
|
177
|
+
type: 'CallExpression',
|
|
178
|
+
callee: { type: 'Identifier', name: '_$stmtCov' },
|
|
179
|
+
arguments: [
|
|
180
|
+
{ type: 'Identifier', name: fileIdVarName },
|
|
181
|
+
{ type: 'NumericLiteral', value: increment.statementId }
|
|
182
|
+
]
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Returns the call expression from `cov_2pvvu1hl8v().b[2][0]++;` if
|
|
187
|
+
* the given UpdateExpression is a branch coverage update expression.
|
|
188
|
+
*/
|
|
189
|
+
function extractBranchCoverageIncrement(expr) {
|
|
190
|
+
if (expr.operator === '++' &&
|
|
191
|
+
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
192
|
+
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
193
|
+
(0, types_1.isMemberExpression)(expr.argument.object.object) &&
|
|
194
|
+
(0, types_1.isCallExpression)(expr.argument.object.object.object) &&
|
|
195
|
+
isCoverageObjectCall(expr.argument.object.object.object)) {
|
|
196
|
+
const coverageObjectId = expr.argument.object.object.object.callee.name;
|
|
197
|
+
const branchId = expr.argument.object.property.value;
|
|
198
|
+
const locationId = expr.argument.property.value;
|
|
199
|
+
return { type: 'branch', branchId, locationId, coverageObjectId };
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Returns the call expression from `cov_104fq7oo4i().s[0]++;` if
|
|
205
|
+
* the given UpdateExpression is a statement coverage update expression.
|
|
206
|
+
*/
|
|
207
|
+
function extractStatementCoverageIncrement(expr) {
|
|
208
|
+
if (expr.operator === '++' &&
|
|
209
|
+
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
210
|
+
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
211
|
+
(0, types_1.isCallExpression)(expr.argument.object.object) &&
|
|
212
|
+
(0, types_1.isIdentifier)(expr.argument.object.property) &&
|
|
213
|
+
expr.argument.object.property.name === 's' &&
|
|
214
|
+
isCoverageObjectCall(expr.argument.object.object)) {
|
|
215
|
+
const coverageObjectId = expr.argument.object.object.callee.name;
|
|
216
|
+
const statementId = expr.argument.property.value;
|
|
217
|
+
return { type: 'statement', statementId, coverageObjectId };
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Returns the call expression from `cov_104fq7oo4i().f[0]++;` if
|
|
223
|
+
* the given UpdateExpression is a function coverage update expression.
|
|
224
|
+
*/
|
|
225
|
+
function extractFunctionCoverageIncrement(expr) {
|
|
226
|
+
if (expr.operator === '++' &&
|
|
227
|
+
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
228
|
+
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
229
|
+
(0, types_1.isCallExpression)(expr.argument.object.object) &&
|
|
230
|
+
(0, types_1.isIdentifier)(expr.argument.object.property) &&
|
|
231
|
+
expr.argument.object.property.name === 'f' &&
|
|
232
|
+
isCoverageObjectCall(expr.argument.object.object)) {
|
|
233
|
+
const coverageObjectId = expr.argument.object.object.callee.name;
|
|
234
|
+
const functionId = expr.argument.property.value;
|
|
235
|
+
return { type: 'function', functionId, coverageObjectId };
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Given an `UpdateExpression` extract the call expression returning the coverage object.
|
|
241
|
+
*/
|
|
242
|
+
function extractCoverageIncrement(expr) {
|
|
243
|
+
var _a, _b;
|
|
244
|
+
return ((_b = (_a = extractBranchCoverageIncrement(expr)) !== null && _a !== void 0 ? _a : extractStatementCoverageIncrement(expr)) !== null && _b !== void 0 ? _b : extractFunctionCoverageIncrement(expr));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Check if the given call expression is a coverage call expression.
|
|
248
|
+
* If this is not the case return `undefined`, and the call expression itself otherwise.
|
|
249
|
+
*/
|
|
250
|
+
function isCoverageObjectCall(callExpression) {
|
|
251
|
+
return (callExpression !== undefined &&
|
|
252
|
+
(0, types_1.isIdentifier)(callExpression.callee) &&
|
|
253
|
+
callExpression.callee.name.startsWith(COVERAGE_OBJ_FUNCTION_NAME_PREFIX));
|
|
254
|
+
}
|
package/dist/vaccine.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var
|
|
1
|
+
(()=>{var N=Object.create;var f=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var U=e=>f(e,"__esModule",{value:!0});var C=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var B=(e,t,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of L(t))!D.call(e,o)&&o!=="default"&&f(e,o,{get:()=>t[o],enumerable:!(n=x(t,o))||n.enumerable});return e},E=e=>B(U(f(e!=null?N(I(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var b=C((Z,w)=>{w.exports=!1});var v=C(()=>{});function p(e){let t=new Blob([e],{type:"text/javascript"}),n=URL.createObjectURL(t),o=new Worker(n);return URL.revokeObjectURL(n),o}function m(){return p('var i=class{constructor(e){this.cachedMessages=[];this.url=e,this.socket=this.createSocket()}createSocket(){let e=new WebSocket(this.url);return e.onopen=()=>this.onopen(),e.onclose=()=>this.onclose(),e}onclose(){this.socket=this.createSocket()}onopen(){console.log("Connection to Coverage Collector established."),this.cachedMessages.forEach(e=>this.socket.send(e)),this.cachedMessages=[]}send(e){this.socket.readyState===WebSocket.OPEN?this.socket.send(e):(this.cachedMessages.push(e),this.cachedMessages.length%500===0&&console.log(`More than ${this.cachedMessages.length} messages are queued to be sent.`))}};var E=20,v=1e3,d=class{constructor(e,t){this.milliseconds=e;this.onCountedToZero=t;this.timerHandle=null}restartCountdown(){this.stopCountdown(),this.timerHandle=self.setTimeout(()=>{this.stopCountdown(),this.onCountedToZero()},this.milliseconds)}stopCountdown(){this.timerHandle!==null&&(self.clearTimeout(this.timerHandle),this.timerHandle=null)}},a=class{constructor(e){this.socket=e,this.cachedCoveredRanges=new Map,this.numberOfCachedPositions=0,this.flushCountdown=new d(v,()=>this.flush())}addRange(e,t){let o=this.cachedCoveredRanges.get(e);o||(o=new Set,this.cachedCoveredRanges.set(e,o)),o.add(t),this.numberOfCachedPositions+=1,this.flushCountdown.restartCountdown(),this.numberOfCachedPositions>=E&&this.flush()}flush(){this.numberOfCachedPositions!==0&&(this.flushCountdown.stopCountdown(),this.cachedCoveredRanges.forEach((e,t)=>{let o=Array.from(e).map(s=>`${s.start.line}:${s.start.column}:${s.end.line}:${s.end.column}`);this.socket.send(`${"c"} ${t} ${o.join(" ")}`),e.clear()}),this.cachedCoveredRanges.clear(),this.numberOfCachedPositions=0)}};var C="s",m="b";console.log("Starting coverage forwarding worker.");var p=new i("$REPORT_TO_URL/socket"),h=new a(p),l=new Map;onmessage=n=>{let e=n.data;if(e.startsWith("s"))p.send(e);else if(e.startsWith("i")){let t=JSON.parse(e.substring(2));l.set(t.hash,t),console.info(`Received coverage mapping information for "${t.hash}".`)}else e.startsWith("u")?S(e):e==="unload"?h.flush():console.error(`No handler for message: ${e}`)};function S(n){var u;let e=n.split(" ");if(e.length<4||l===null)return;let t=e[1],o=e[2],s=l.get(t);if(!s){console.log(`No coverage mapping information for ${t} available!`);return}if(o===C){let c=e[3],r=s.statementMap[c];r&&h.addRange(t,r)}else if(o===m){let c=e[3],r=Number.parseInt(e[4]),g=(u=s.branchMap[c])==null?void 0:u.locations[r];g&&h.addRange(t,g)}}\n')}var k=E(b());function G(e){if(!(typeof WorkerGlobalScope=="function"&&self instanceof WorkerGlobalScope)){if(typeof window.addEventListener!="function")return;window.addEventListener("beforeunload",function(){e()},!0),window.addEventListener("unload",function(){e()},!0)}}var S={add:G};var R=E(v()),j=k.default?R.default:S,a=new Set,_=!1;function V(){_||(_=!0,j.add(H))}function O(e){if(V(),typeof e!="function")throw new Error("Listener is no function");a.add(e);var t={remove:function(){return a.delete(e)},run:function(){return a.delete(e),e()}};return t}function H(){var e=[];return a.forEach(function(t){e.push(t()),a.delete(t)}),Promise.all(e)}function i(){return u()}function $(){return typeof window!="undefined"}function u(){return window}function g(e,t){let n=i()[e];return n||(n=t,i()[e]=n),n}var s;(function(r){r.MESSAGE_TYPE_SOURCEMAP="s",r.MESSAGE_TYPE_COVERAGE="c",r.ISTANBUL_COV_OBJECT="i",r.UNRESOLVED_CODE_ENTITY="u"})(s||(s={}));var W="s",M="b";var T=g("__TS_AGENT",{});function d(){return T._$BcWorker}function y(e){return T._$BcWorker=e,e}var A=new Set;i()._$stmtCov=function(e,t){d().postMessage(`${s.UNRESOLVED_CODE_ENTITY} ${e} ${W} ${t}`)};i()._$brCov=function(e,t,n){d().postMessage(`${s.UNRESOLVED_CODE_ENTITY} ${e} ${M} ${t} ${n}`)};i()._$registerCoverageObject=function(e){let t=e.hash;if(A.has(t)){console.log(`Coverage interceptor added twice for ${t}. This seems to be a bug in the instrumentation.`);return}else A.add(t);if(!d()){let n=y(new m);(function(){let r=function(l){let c=u()[l];u()[l]=function(...h){if(n.postMessage("unload"),c)return c.apply(this,h)},$()&&Object.defineProperty(u(),l,{get:function(){return c},set:function(h){c=h}})};r("onunload"),r("onbeforeunload"),O(()=>n.postMessage("unload"))})()}(function(){d().postMessage(`${s.ISTANBUL_COV_OBJECT} ${JSON.stringify(e)}`);let o=g("sentMaps",new Set);e.inputSourceMap&&(o.has(e.path)||(d().postMessage(`${s.MESSAGE_TYPE_SOURCEMAP} ${t}:${JSON.stringify(e.inputSourceMap)}`),o.add(e.path)))})()};})();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamscale/javascript-instrumenter",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.1-beta.42",
|
|
4
|
+
"description": "JavaScript coverage instrumenter with coverage forwarding to a collector process",
|
|
5
5
|
"main": "dist/src/main.js",
|
|
6
6
|
"bin": "dist/src/main.js",
|
|
7
7
|
"types": "dist/src/main.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@babel/generator": "^7.18.2",
|
|
51
51
|
"@babel/parser": "^7.18.5",
|
|
52
|
-
"@babel/traverse": "^7.18.
|
|
52
|
+
"@babel/traverse": "^7.18.6",
|
|
53
53
|
"@babel/types": "^7.18.4",
|
|
54
54
|
"@cqse/commons": "^0.0.1-beta.1",
|
|
55
55
|
"@types/micromatch": "^4.0.2",
|
|
@@ -62,7 +62,6 @@
|
|
|
62
62
|
"istanbul-lib-instrument": "^5.2.0",
|
|
63
63
|
"micromatch": "4.0.4",
|
|
64
64
|
"mkdirp": "^1.0.4",
|
|
65
|
-
"nyc": "^15.1.0",
|
|
66
65
|
"source-map": "0.7.3",
|
|
67
66
|
"typescript-optional": "^2.0.1",
|
|
68
67
|
"unload": "^2.2.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Cleaner.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Cleaner.ts"],"names":[],"mappings":"AAGA,OAAO,EAKN,cAAc,EAEd,MAAM,cAAc,CAAC;AAMtB;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,GAClD,MAAM,CAcR"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.cleanSourceCode = void 0;
|
|
7
|
-
const parser_1 = require("@babel/parser");
|
|
8
|
-
const generator_1 = __importDefault(require("@babel/generator"));
|
|
9
|
-
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
10
|
-
const types_1 = require("@babel/types");
|
|
11
|
-
function isUpdateExpressionPath(path) {
|
|
12
|
-
return path.node.type === 'UpdateExpression';
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Remove IstanbulJs instrumentations based on the given
|
|
16
|
-
* hook `makeCoverable`.
|
|
17
|
-
*
|
|
18
|
-
* An instrumentation is removed if the hook `makeCoverable` returns `false`.
|
|
19
|
-
*/
|
|
20
|
-
function cleanSourceCode(code, esModules, makeCoverable) {
|
|
21
|
-
const ast = (0, parser_1.parse)(code, { sourceType: esModules ? 'module' : 'script' });
|
|
22
|
-
(0, traverse_1.default)(ast, {
|
|
23
|
-
enter(path) {
|
|
24
|
-
if (isUpdateExpressionPath(path)) {
|
|
25
|
-
if (isCoverageIncrementNode(path)) {
|
|
26
|
-
if (path.node.loc && !makeCoverable(path.node.loc)) {
|
|
27
|
-
path.remove();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
return (0, generator_1.default)(ast, {}, code).code;
|
|
34
|
-
}
|
|
35
|
-
exports.cleanSourceCode = cleanSourceCode;
|
|
36
|
-
/**
|
|
37
|
-
* Checks if the given `path.node` to a statement like `cov_104fq7oo4i().f[0]++;`
|
|
38
|
-
*/
|
|
39
|
-
function isCoverageIncrementNode(path) {
|
|
40
|
-
return extractCoverageCallExpression(path.node) !== undefined;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Returns the call expression from `cov_2pvvu1hl8v().b[2][0]++;` if
|
|
44
|
-
* the given UpdateExpression is a branch coverage update expression.
|
|
45
|
-
*/
|
|
46
|
-
function extractBranchCounterExpression(expr) {
|
|
47
|
-
if (expr.operator === '++' &&
|
|
48
|
-
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
49
|
-
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
50
|
-
(0, types_1.isMemberExpression)(expr.argument.object.object) &&
|
|
51
|
-
(0, types_1.isCallExpression)(expr.argument.object.object.object)) {
|
|
52
|
-
// Branch counter
|
|
53
|
-
return extractCoverageObjectCall(expr.argument.object.object.object);
|
|
54
|
-
}
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Returns the call expression from `cov_104fq7oo4i().f[0]++;` if
|
|
59
|
-
* the given UpdateExpression is a function or statement coverage update expression.
|
|
60
|
-
*/
|
|
61
|
-
function extractFunctionOrStatementCounterExpression(expr) {
|
|
62
|
-
if (expr.operator === '++' &&
|
|
63
|
-
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
64
|
-
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
65
|
-
(0, types_1.isCallExpression)(expr.argument.object.object)) {
|
|
66
|
-
// Function and statement counter
|
|
67
|
-
return extractCoverageObjectCall(expr.argument.object.object);
|
|
68
|
-
}
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Given an `UpdateExpression` extract the call expression returning the coverage object.
|
|
73
|
-
*/
|
|
74
|
-
function extractCoverageCallExpression(expr) {
|
|
75
|
-
var _a;
|
|
76
|
-
return (_a = extractBranchCounterExpression(expr)) !== null && _a !== void 0 ? _a : extractFunctionOrStatementCounterExpression(expr);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Check if the given call expression is a coverage call expression.
|
|
80
|
-
* If this is not the case return `undefined`, and the call expression itself otherwise.
|
|
81
|
-
*/
|
|
82
|
-
function extractCoverageObjectCall(callExpression) {
|
|
83
|
-
if (callExpression && (0, types_1.isIdentifier)(callExpression.callee) && callExpression.callee.name.startsWith('cov_')) {
|
|
84
|
-
return callExpression;
|
|
85
|
-
}
|
|
86
|
-
return undefined;
|
|
87
|
-
}
|