@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 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.41",
4
- "description": "Istanbul-based coverage instrumenter with coverage forwarding via WebSockets",
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.5",
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;YAuGR,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"}
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 Cleaner_1 = require("./Cleaner");
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=makeCoverageInterceptor(coverage[path])')
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, Cleaner_1.cleanSourceCode)(instrumentedSource, configurationAlternative.esModules, location => {
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=Cleaner.d.ts.map
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 L=Object.create;var p=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var P=e=>p(e,"__esModule",{value:!0});var E=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var j=(e,t,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of H(t))!B.call(e,r)&&r!=="default"&&p(e,r,{get:()=>t[r],enumerable:!(n=D(t,r))||n.enumerable});return e},S=e=>j(P(p(e!=null?L(U(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var O=E((re,R)=>{R.exports=!1});var $=E(()=>{});function m(e){let t=new Blob([e],{type:"text/javascript"}),n=URL.createObjectURL(t),r=new Worker(n);return URL.revokeObjectURL(n),r}function g(){return m('var a=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,g=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)}},c=class{constructor(e){this.socket=e,this.cachedCoveredRanges=new Map,this.numberOfCachedPositions=0,this.flushCountdown=new g(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(n=>`${n.start.line}:${n.start.column}:${n.end.line}:${n.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 a("$REPORT_TO_URL/socket"),d=new c(p),h=new Map;onmessage=s=>{let e=s.data;if(e.startsWith("s"))p.send(e);else if(e.startsWith("i")){let t=JSON.parse(e.substring(2));h.set(t.hash,t),console.info(`Received coverage mapping information for "${t.hash}".`)}else e.startsWith("u")?S(e):e==="unload"?d.flush():console.error(`No handler for message: ${e}`)};function S(s){var l;let e=s.split(" ");if(e.length<4||h===null)return;let t=e[1],o=e[2],n=h.get(t);if(!n){console.log(`No coverage mapping information for ${t} available!`);return}if(o===C){let i=e[3],r=n.statementMap[i];r&&d.addRange(t,r)}else if(o===m){let i=e[3],r=Number.parseInt(e[4]),u=(l=n.branchMap[i])==null?void 0:l.locations[r];u&&d.addRange(t,u)}}\n')}var b="s",w="b";var a;(function(o){o.MESSAGE_TYPE_SOURCEMAP="s",o.MESSAGE_TYPE_COVERAGE="c",o.ISTANBUL_COV_OBJECT="i",o.UNRESOLVED_CODE_ENTITY="u"})(a||(a={}));var k=class{constructor(t,n,r){this.worker=t;this.fileHash=n;this.path=r}get(t,n,r){let o=t[n];return o!==Object(o)?o:C(this.worker,this.fileHash,o,[...this.path,n])}set(t,n,r){let o=[...this.path,n],i=o[0];if(i===b){let s=o[1];this.worker.postMessage(`${a.UNRESOLVED_CODE_ENTITY} ${this.fileHash} ${b} ${s}`)}else if(i===w){let s=o[1],u=o[2];this.worker.postMessage(`${a.UNRESOLVED_CODE_ENTITY} ${this.fileHash} ${w} ${s} ${u}`)}return!0}};function C(e,t,n,r){return new Proxy(n,new k(e,t,r))}var T=S(O());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 _={add:G};var W=S($()),V=T.default?W.default:_,c=new Set,M=!1;function Y(){M||(M=!0,V.add(J))}function x(e){if(Y(),typeof e!="function")throw new Error("Listener is no function");c.add(e);var t={remove:function(){return c.delete(e)},run:function(){return c.delete(e),e()}};return t}function J(){var e=[];return c.forEach(function(t){e.push(t()),c.delete(t)}),Promise.all(e)}function l(){return d()}function A(){return typeof window!="undefined"}function d(){return window}function v(e,t){let n=l()[e];return n||(n=t,l()[e]=n),n}var I=v("__TS_AGENT",{});function h(){return I._$BcWorker}function z(e){return I._$BcWorker=e,e}var N=new Set;l().makeCoverageInterceptor=function(e){let t=e.hash;if(N.has(t)){console.log(`Coverage interceptor added twice for ${t}. This seems to be a bug in the instrumentation.`);return}else N.add(t);if(!h()){let n=z(new g);(function(){let o=function(i){let s=d()[i];d()[i]=function(...u){if(n.postMessage("unload"),s)return s.apply(this,u)},A()&&Object.defineProperty(d(),i,{get:function(){return s},set:function(u){s=u}})};o("onunload"),o("onbeforeunload"),x(()=>n.postMessage("unload"))})()}return function(){h().postMessage(`${a.ISTANBUL_COV_OBJECT} ${JSON.stringify(e)}`);let r=v("sentMaps",new Set);e.inputSourceMap&&(r.has(e.path)||(h().postMessage(`${a.MESSAGE_TYPE_SOURCEMAP} ${t}:${JSON.stringify(e.inputSourceMap)}`),r.add(e.path)))}(),function(){let r=new Set;l()._$Bc=(o,i,s,u,y)=>{let f=`${o}:${i}:${s}:${u}:${y}`;r.has(f)||(h().postMessage(f),r.add(f))}}(),C(h(),e.hash,e,[])};})();
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.41",
4
- "description": "Istanbul-based coverage instrumenter with coverage forwarding via WebSockets",
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.5",
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
- }