@teamscale/javascript-instrumenter 0.0.1-beta.39 → 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 +11 -12
- package/dist/src/App.d.ts.map +1 -1
- package/dist/src/App.js +3 -0
- package/dist/src/instrumenter/Instrumenter.d.ts +37 -20
- package/dist/src/instrumenter/Instrumenter.d.ts.map +1 -1
- package/dist/src/instrumenter/Instrumenter.js +87 -58
- 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/src/instrumenter/Task.d.ts +2 -1
- package/dist/src/instrumenter/Task.d.ts.map +1 -1
- package/dist/src/instrumenter/Task.js +2 -1
- package/dist/src/instrumenter/TaskBuilder.d.ts +3 -0
- package/dist/src/instrumenter/TaskBuilder.d.ts.map +1 -1
- package/dist/src/instrumenter/TaskBuilder.js +2 -1
- package/dist/vaccine.js +1 -1
- package/package.json +11 -12
- package/dist/src/instrumenter/Cleaner.d.ts.map +0 -1
- package/dist/src/instrumenter/Cleaner.js +0 -82
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",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"build": "tsc --project tsconfig.json && yarn buildVaccine",
|
|
18
18
|
"buildVaccine": "node esbuild.mjs",
|
|
19
19
|
"instrumenter": "node dist/src/main.js",
|
|
20
|
-
"test": "yarn build && NODE_OPTIONS='--experimental-vm-modules' jest --forceExit --coverage --silent=true --detectOpenHandles"
|
|
20
|
+
"test": "yarn build && NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=8192' jest --forceExit --coverage --silent=true --detectOpenHandles"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist/**/*"
|
|
24
24
|
],
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@babel/core": "^7.
|
|
27
|
-
"@babel/plugin-transform-modules-commonjs": "^7.
|
|
28
|
-
"@babel/preset-env": "^7.
|
|
26
|
+
"@babel/core": "^7.18.5",
|
|
27
|
+
"@babel/plugin-transform-modules-commonjs": "^7.18.2",
|
|
28
|
+
"@babel/preset-env": "^7.18.2",
|
|
29
29
|
"@types/async": "^3.2.6",
|
|
30
30
|
"@types/bunyan": "^1.8.8",
|
|
31
31
|
"@types/convert-source-map": "^1.5.1",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"typescript": "^4.4.3"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@babel/generator": "^7.
|
|
51
|
-
"@babel/parser": "^7.
|
|
52
|
-
"@babel/traverse": "^7.
|
|
53
|
-
"@babel/types": "^7.
|
|
50
|
+
"@babel/generator": "^7.18.2",
|
|
51
|
+
"@babel/parser": "^7.18.5",
|
|
52
|
+
"@babel/traverse": "^7.18.6",
|
|
53
|
+
"@babel/types": "^7.18.4",
|
|
54
54
|
"@cqse/commons": "^0.0.1-beta.1",
|
|
55
55
|
"@types/micromatch": "^4.0.2",
|
|
56
56
|
"argparse": "^2.0.1",
|
|
@@ -59,10 +59,9 @@
|
|
|
59
59
|
"convert-source-map": "^1.7.0",
|
|
60
60
|
"foreground-child": "^2.0.0",
|
|
61
61
|
"glob": "^7.1.7",
|
|
62
|
-
"istanbul-lib-instrument": "^
|
|
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",
|
package/dist/src/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/App.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,EAAE,uBAAuB,EAAe,MAAM,4BAA4B,CAAC;AAKlF,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,qBAAa,GAAG;IACf;;;OAGG;WACiB,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;IAa9C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/App.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,EAAE,uBAAuB,EAAe,MAAM,4BAA4B,CAAC;AAKlF,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,qBAAa,GAAG;IACf;;;OAGG;WACiB,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;IAa9C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAqC1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IA8B1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B;;;;;OAKG;WACW,qBAAqB,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAO1G,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAIxC,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAajC"}
|
package/dist/src/App.js
CHANGED
|
@@ -85,6 +85,9 @@ class App {
|
|
|
85
85
|
nargs: '*',
|
|
86
86
|
help: 'Glob pattern(s) of files in the source origin to produce coverage for. Multiple patterns can be separated by space.'
|
|
87
87
|
});
|
|
88
|
+
parser.add_argument('-p', '--dump-origins-to', {
|
|
89
|
+
help: 'Optional location specifying where to dump possible origins from the source map as a json file'
|
|
90
|
+
});
|
|
88
91
|
parser.add_argument('inputs', { nargs: '+', help: 'The input file(s) to instrument.' });
|
|
89
92
|
return parser;
|
|
90
93
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { CollectorSpecifier, InstrumentationTask, OriginSourcePattern, TaskElement, TaskResult } from './Task';
|
|
1
|
+
import { CollectorSpecifier, InstrumentationTask, OriginSourcePattern, SourceMapReference, TaskElement, TaskResult } from './Task';
|
|
2
|
+
import { RawSourceMap, SourceMapConsumer } from 'source-map';
|
|
3
|
+
import { Optional } from 'typescript-optional';
|
|
2
4
|
import Logger from 'bunyan';
|
|
3
5
|
export declare const IS_INSTRUMENTED_TOKEN = "/** $IS_JS_PROFILER_INSTRUMENTED=true **/";
|
|
4
6
|
/**
|
|
@@ -37,25 +39,16 @@ export declare class IstanbulInstrumenter implements IInstrumenter {
|
|
|
37
39
|
* @param collector - The collector to send the coverage information to.
|
|
38
40
|
* @param taskElement - The task element to perform the instrumentation for.
|
|
39
41
|
* @param sourcePattern - A pattern to restrict the instrumentation to only a fraction of the task element.
|
|
42
|
+
* @param dumpOriginsFile - A file path where all origins from the source map should be dumped in json format, or undefined if no origins should be dumped
|
|
40
43
|
*/
|
|
41
|
-
instrumentOne(collector: CollectorSpecifier, taskElement: TaskElement, sourcePattern: OriginSourcePattern): Promise<TaskResult>;
|
|
44
|
+
instrumentOne(collector: CollectorSpecifier, taskElement: TaskElement, sourcePattern: OriginSourcePattern, dumpOriginsFile: string | undefined): Promise<TaskResult>;
|
|
42
45
|
private removeUnwantedInstrumentation;
|
|
43
|
-
private loadSourceMap;
|
|
44
46
|
/**
|
|
45
47
|
* Loads the vaccine from the vaccine file and adjusts some template parameters.
|
|
46
48
|
*
|
|
47
49
|
* @param collector - The collector to send coverage information to.
|
|
48
50
|
*/
|
|
49
51
|
private loadVaccine;
|
|
50
|
-
/**
|
|
51
|
-
* Should the given file be excluded from the instrumentation,
|
|
52
|
-
* based on the source files that have been transpiled into it?
|
|
53
|
-
*
|
|
54
|
-
* @param pattern - The pattern to match the origin source files.
|
|
55
|
-
* @param sourceFile - The bundle file name.
|
|
56
|
-
* @param originSourceFiles - The list of files that were transpiled into the bundle.
|
|
57
|
-
*/
|
|
58
|
-
private shouldExcludeFromInstrumentation;
|
|
59
52
|
/**
|
|
60
53
|
* @returns whether the given file is supported for instrumentation.
|
|
61
54
|
*/
|
|
@@ -65,13 +58,37 @@ export declare class IstanbulInstrumenter implements IInstrumenter {
|
|
|
65
58
|
* given task element.
|
|
66
59
|
*/
|
|
67
60
|
private configurationAlternativesFor;
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
* @param taskFile - The name of the file the `inputSource` is from.
|
|
73
|
-
* @param externalSourceMapFile - An external source map file to consider.
|
|
74
|
-
*/
|
|
75
|
-
private loadInputSourceMap;
|
|
61
|
+
/** Appends all origins from the source map to a given file. Creates the file if it does not exist yet. */
|
|
62
|
+
private dumpOrigins;
|
|
63
|
+
/** Clears the dump origins file if it exists, such that it is now ready to be appended for every instrumented file. */
|
|
64
|
+
private clearDumpOriginsFileIfNeeded;
|
|
76
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Extract the sourcemap from the given source code.
|
|
68
|
+
*
|
|
69
|
+
* @param instrumentedSource - The source code.
|
|
70
|
+
* @param instrumentedSourceFileName - The file name to assume for the file name.
|
|
71
|
+
*/
|
|
72
|
+
export declare function loadSourceMap(instrumentedSource: string, instrumentedSourceFileName: string): Promise<SourceMapConsumer | undefined>;
|
|
73
|
+
/**
|
|
74
|
+
* Given a source code file, load the corresponding sourcemap.
|
|
75
|
+
*
|
|
76
|
+
* @param inputSource - The source code that might contain sourcemap comments.
|
|
77
|
+
* @param taskFile - The name of the file the `inputSource` is from.
|
|
78
|
+
* @param externalSourceMapFile - An external source map file to consider.
|
|
79
|
+
*/
|
|
80
|
+
export declare function loadInputSourceMap(inputSource: string, taskFile: string, externalSourceMapFile: Optional<SourceMapReference>): RawSourceMap | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Extract a sourcemap for a given code comment.
|
|
83
|
+
*
|
|
84
|
+
* @param sourcecode - The source code that is scanned for source map comments.
|
|
85
|
+
* @param sourceFilePath - The file name the code was loaded from.
|
|
86
|
+
*/
|
|
87
|
+
export declare function sourceMapFromCodeComment(sourcecode: string, sourceFilePath: string): RawSourceMap | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Read a source map from a source map file.
|
|
90
|
+
*
|
|
91
|
+
* @param mapFilePath
|
|
92
|
+
*/
|
|
93
|
+
export declare function sourceMapFromMapFile(mapFilePath: string): RawSourceMap | undefined;
|
|
77
94
|
//# sourceMappingURL=Instrumenter.d.ts.map
|
|
@@ -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,
|
|
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"}
|
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.IstanbulInstrumenter = exports.IS_INSTRUMENTED_TOKEN = void 0;
|
|
29
|
+
exports.sourceMapFromMapFile = exports.sourceMapFromCodeComment = exports.loadInputSourceMap = exports.loadSourceMap = exports.IstanbulInstrumenter = exports.IS_INSTRUMENTED_TOKEN = void 0;
|
|
30
30
|
const Task_1 = require("./Task");
|
|
31
31
|
const commons_1 = require("@cqse/commons");
|
|
32
32
|
const source_map_1 = require("source-map");
|
|
@@ -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 **/';
|
|
@@ -52,11 +52,12 @@ class IstanbulInstrumenter {
|
|
|
52
52
|
* {@inheritDoc #IInstrumenter.instrument}
|
|
53
53
|
*/
|
|
54
54
|
async instrument(task) {
|
|
55
|
+
this.clearDumpOriginsFileIfNeeded(task.dumpOriginsFile);
|
|
55
56
|
// We limit the number of instrumentations in parallel to one to
|
|
56
57
|
// not overuse memory (NodeJS has only limited mem to use).
|
|
57
58
|
return async_1.default
|
|
58
59
|
.mapLimit(task.elements, 1, async (taskElement) => {
|
|
59
|
-
return await this.instrumentOne(task.collector, taskElement, task.originSourcePattern);
|
|
60
|
+
return await this.instrumentOne(task.collector, taskElement, task.originSourcePattern, task.dumpOriginsFile);
|
|
60
61
|
})
|
|
61
62
|
.then(results => {
|
|
62
63
|
return results.reduce((prev, curr) => {
|
|
@@ -70,8 +71,9 @@ class IstanbulInstrumenter {
|
|
|
70
71
|
* @param collector - The collector to send the coverage information to.
|
|
71
72
|
* @param taskElement - The task element to perform the instrumentation for.
|
|
72
73
|
* @param sourcePattern - A pattern to restrict the instrumentation to only a fraction of the task element.
|
|
74
|
+
* @param dumpOriginsFile - A file path where all origins from the source map should be dumped in json format, or undefined if no origins should be dumped
|
|
73
75
|
*/
|
|
74
|
-
async instrumentOne(collector, taskElement, sourcePattern) {
|
|
76
|
+
async instrumentOne(collector, taskElement, sourcePattern, dumpOriginsFile) {
|
|
75
77
|
var _a, _b;
|
|
76
78
|
const inputFileSource = fs.readFileSync(taskElement.fromFile, 'utf8');
|
|
77
79
|
// We skip files that we have already instrumented
|
|
@@ -97,32 +99,32 @@ class IstanbulInstrumenter {
|
|
|
97
99
|
let inputSourceMap;
|
|
98
100
|
try {
|
|
99
101
|
const instrumenter = istanbul.createInstrumenter(configurationAlternative);
|
|
100
|
-
inputSourceMap =
|
|
102
|
+
inputSourceMap = loadInputSourceMap(inputFileSource, taskElement.fromFile, taskElement.externalSourceMapFile);
|
|
101
103
|
// Based on the source maps of the file to instrument, we can now
|
|
102
104
|
// decide if we should NOT write an instrumented version of it
|
|
103
105
|
// and use the original code instead and write it to the target path.
|
|
104
106
|
//
|
|
105
107
|
const originSourceFiles = (_a = inputSourceMap === null || inputSourceMap === void 0 ? void 0 : inputSourceMap.sources) !== null && _a !== void 0 ? _a : [];
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
return new Task_1.TaskResult(0, 1, 0, 0, 0, 0, 0);
|
|
108
|
+
if (dumpOriginsFile) {
|
|
109
|
+
this.dumpOrigins(dumpOriginsFile, originSourceFiles);
|
|
109
110
|
}
|
|
110
111
|
// The main instrumentation (adding coverage statements) is performed now:
|
|
111
|
-
instrumentedSource = instrumenter
|
|
112
|
-
.instrumentSync(inputFileSource, taskElement.fromFile, inputSourceMap)
|
|
113
|
-
.replace(/actualCoverage\s*=\s*coverage\[path\]/g, 'actualCoverage=makeCoverageInterceptor(coverage[path])')
|
|
114
|
-
.replace(/new Function\("return this"\)\(\)/g, "typeof window === 'object' ? window : this");
|
|
112
|
+
instrumentedSource = instrumenter.instrumentSync(inputFileSource, taskElement.fromFile, inputSourceMap);
|
|
115
113
|
this.logger.debug('Instrumentation source maps to:', (_b = instrumenter.lastSourceMap()) === null || _b === void 0 ? void 0 : _b.sources);
|
|
116
114
|
// In case of a bundle, the initial instrumentation step might have added
|
|
117
115
|
// too much and undesired instrumentations. Remove them now.
|
|
118
|
-
|
|
116
|
+
const instrumentedSourcemap = instrumenter.lastSourceMap();
|
|
117
|
+
let instrumentedAndCleanedSource = await this.removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern, instrumentedSourcemap);
|
|
118
|
+
instrumentedAndCleanedSource = instrumentedAndCleanedSource
|
|
119
|
+
.replace(/actualCoverage\s*=\s*coverage\[path\]/g, 'actualCoverage=_$registerCoverageObject(coverage[path])')
|
|
120
|
+
.replace(/new Function\("return this"\)\(\)/g, "typeof window === 'object' ? window : this");
|
|
119
121
|
// The process also can result in a new source map that we will append in the result.
|
|
120
122
|
//
|
|
121
123
|
// `lastSourceMap` === Sourcemap for the last file that was instrumented.
|
|
122
124
|
finalSourceMap = convertSourceMap.fromObject(instrumenter.lastSourceMap()).toComment();
|
|
123
125
|
// We now can glue together the final version of the instrumented file.
|
|
124
126
|
const vaccineSource = this.loadVaccine(collector);
|
|
125
|
-
writeToFile(taskElement.toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${vaccineSource} ${
|
|
127
|
+
writeToFile(taskElement.toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${vaccineSource} ${instrumentedAndCleanedSource} \n${finalSourceMap}`);
|
|
126
128
|
return new Task_1.TaskResult(1, 0, 0, 0, 0, 0, 0);
|
|
127
129
|
}
|
|
128
130
|
catch (e) {
|
|
@@ -139,35 +141,36 @@ class IstanbulInstrumenter {
|
|
|
139
141
|
}
|
|
140
142
|
return new Task_1.TaskResult(0, 0, 0, 0, 0, 1, 0);
|
|
141
143
|
}
|
|
142
|
-
async removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern) {
|
|
143
|
-
|
|
144
|
-
const instrumentedSourceMapConsumer = await this.loadSourceMap(instrumentedSource, taskElement.fromFile);
|
|
144
|
+
async removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern, instrumentedSourcemap) {
|
|
145
|
+
const instrumentedSourceMapConsumer = await new source_map_1.SourceMapConsumer(instrumentedSourcemap);
|
|
145
146
|
// Without a source map, excludes/includes do not work.
|
|
146
147
|
if (!instrumentedSourceMapConsumer) {
|
|
147
148
|
return instrumentedSource;
|
|
148
149
|
}
|
|
150
|
+
const removedInstrumentationFor = new Set();
|
|
149
151
|
// Remove the unwanted instrumentation
|
|
150
|
-
const cleaned = (0,
|
|
152
|
+
const cleaned = (0, Postprocessor_1.cleanSourceCode)(instrumentedSource, configurationAlternative.esModules, location => {
|
|
151
153
|
const originalPosition = instrumentedSourceMapConsumer.originalPositionFor({
|
|
152
154
|
line: location.start.line,
|
|
153
155
|
column: location.start.column
|
|
154
156
|
});
|
|
155
157
|
if (!originalPosition.source) {
|
|
156
|
-
return
|
|
158
|
+
return false;
|
|
157
159
|
}
|
|
158
|
-
|
|
160
|
+
const isToCover = sourcePattern.isAnyIncluded([originalPosition.source]);
|
|
161
|
+
if (!isToCover) {
|
|
162
|
+
removedInstrumentationFor.add(originalPosition.source);
|
|
163
|
+
}
|
|
164
|
+
return isToCover;
|
|
159
165
|
});
|
|
166
|
+
if (removedInstrumentationFor.size) {
|
|
167
|
+
this.logger.info(`Removed from ${taskElement.toFile} instrumentation for:`);
|
|
168
|
+
removedInstrumentationFor.forEach(entry => this.logger.info(entry));
|
|
169
|
+
}
|
|
160
170
|
// Explicitly free the source map to avoid memory leaks
|
|
161
171
|
instrumentedSourceMapConsumer.destroy();
|
|
162
172
|
return cleaned;
|
|
163
173
|
}
|
|
164
|
-
async loadSourceMap(instrumentedSource, instrumentedSourceFileName) {
|
|
165
|
-
const instrumentedSourceMap = this.loadInputSourceMap(instrumentedSource, instrumentedSourceFileName, typescript_optional_1.Optional.empty());
|
|
166
|
-
if (instrumentedSourceMap) {
|
|
167
|
-
return await new source_map_1.SourceMapConsumer(instrumentedSourceMap);
|
|
168
|
-
}
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
174
|
/**
|
|
172
175
|
* Loads the vaccine from the vaccine file and adjusts some template parameters.
|
|
173
176
|
*
|
|
@@ -178,17 +181,6 @@ class IstanbulInstrumenter {
|
|
|
178
181
|
// actual values, for example, the collector to send the coverage information to.
|
|
179
182
|
return fs.readFileSync(this.vaccineFilePath, 'utf8').replace(/\$REPORT_TO_URL/g, collector.url);
|
|
180
183
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Should the given file be excluded from the instrumentation,
|
|
183
|
-
* based on the source files that have been transpiled into it?
|
|
184
|
-
*
|
|
185
|
-
* @param pattern - The pattern to match the origin source files.
|
|
186
|
-
* @param sourceFile - The bundle file name.
|
|
187
|
-
* @param originSourceFiles - The list of files that were transpiled into the bundle.
|
|
188
|
-
*/
|
|
189
|
-
shouldExcludeFromInstrumentation(pattern, sourceFile, originSourceFiles) {
|
|
190
|
-
return !pattern.isAnyIncluded(originSourceFiles);
|
|
191
|
-
}
|
|
192
184
|
/**
|
|
193
185
|
* @returns whether the given file is supported for instrumentation.
|
|
194
186
|
*/
|
|
@@ -204,34 +196,69 @@ class IstanbulInstrumenter {
|
|
|
204
196
|
this.logger.debug(`Determining configuration alternatives for ${taskElement.fromFile}`);
|
|
205
197
|
const baseConfig = {
|
|
206
198
|
coverageVariable: '__coverage__',
|
|
207
|
-
produceSourceMap:
|
|
199
|
+
produceSourceMap: 'both'
|
|
208
200
|
};
|
|
209
201
|
return [
|
|
210
202
|
{ ...baseConfig, ...{ esModules: true } },
|
|
211
203
|
{ ...baseConfig, ...{ esModules: false } }
|
|
212
204
|
];
|
|
213
205
|
}
|
|
214
|
-
/**
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
206
|
+
/** Appends all origins from the source map to a given file. Creates the file if it does not exist yet. */
|
|
207
|
+
dumpOrigins(dumpOriginsFile, originSourceFiles) {
|
|
208
|
+
const jsonContent = JSON.stringify(originSourceFiles, null, 2);
|
|
209
|
+
fs.writeFile(dumpOriginsFile, jsonContent + '\n', { flag: 'a' }, error => {
|
|
210
|
+
if (error) {
|
|
211
|
+
this.logger.warn('Could not dump origins file');
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/** Clears the dump origins file if it exists, such that it is now ready to be appended for every instrumented file. */
|
|
216
|
+
clearDumpOriginsFileIfNeeded(dumpOriginsFile) {
|
|
217
|
+
if (dumpOriginsFile && fs.existsSync(dumpOriginsFile)) {
|
|
218
|
+
try {
|
|
219
|
+
fs.unlinkSync(dumpOriginsFile);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
this.logger.warn('Could not clear origins file: ' + err);
|
|
226
223
|
}
|
|
227
|
-
return sourceMapFromMapFile(sourceMapOrigin.sourceMapFilePath);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
return sourceMapFromCodeComment(inputSourceCode, taskFile);
|
|
231
224
|
}
|
|
232
225
|
}
|
|
233
226
|
}
|
|
234
227
|
exports.IstanbulInstrumenter = IstanbulInstrumenter;
|
|
228
|
+
/**
|
|
229
|
+
* Extract the sourcemap from the given source code.
|
|
230
|
+
*
|
|
231
|
+
* @param instrumentedSource - The source code.
|
|
232
|
+
* @param instrumentedSourceFileName - The file name to assume for the file name.
|
|
233
|
+
*/
|
|
234
|
+
async function loadSourceMap(instrumentedSource, instrumentedSourceFileName) {
|
|
235
|
+
const instrumentedSourceMap = loadInputSourceMap(instrumentedSource, instrumentedSourceFileName, typescript_optional_1.Optional.empty());
|
|
236
|
+
if (instrumentedSourceMap) {
|
|
237
|
+
return await new source_map_1.SourceMapConsumer(instrumentedSourceMap);
|
|
238
|
+
}
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
exports.loadSourceMap = loadSourceMap;
|
|
242
|
+
/**
|
|
243
|
+
* Given a source code file, load the corresponding sourcemap.
|
|
244
|
+
*
|
|
245
|
+
* @param inputSource - The source code that might contain sourcemap comments.
|
|
246
|
+
* @param taskFile - The name of the file the `inputSource` is from.
|
|
247
|
+
* @param externalSourceMapFile - An external source map file to consider.
|
|
248
|
+
*/
|
|
249
|
+
function loadInputSourceMap(inputSource, taskFile, externalSourceMapFile) {
|
|
250
|
+
if (externalSourceMapFile.isPresent()) {
|
|
251
|
+
const sourceMapOrigin = externalSourceMapFile.get();
|
|
252
|
+
if (!(sourceMapOrigin instanceof Task_1.SourceMapFileReference)) {
|
|
253
|
+
throw new commons_1.IllegalArgumentException('Type of source map not yet supported!');
|
|
254
|
+
}
|
|
255
|
+
return sourceMapFromMapFile(sourceMapOrigin.sourceMapFilePath);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
return sourceMapFromCodeComment(inputSource, taskFile);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
exports.loadInputSourceMap = loadInputSourceMap;
|
|
235
262
|
/**
|
|
236
263
|
* Extract a sourcemap for a given code comment.
|
|
237
264
|
*
|
|
@@ -265,11 +292,11 @@ function sourceMapFromCodeComment(sourcecode, sourceFilePath) {
|
|
|
265
292
|
// One JS file can refer to several source map files in its comments.
|
|
266
293
|
failedLoading++;
|
|
267
294
|
}
|
|
268
|
-
if (result) {
|
|
269
|
-
return result;
|
|
270
|
-
}
|
|
271
295
|
}
|
|
272
296
|
} while (matched);
|
|
297
|
+
if (result) {
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
273
300
|
if (failedLoading > 0) {
|
|
274
301
|
throw new commons_1.IllegalArgumentException('None of the referenced source map files loaded!');
|
|
275
302
|
}
|
|
@@ -277,6 +304,7 @@ function sourceMapFromCodeComment(sourcecode, sourceFilePath) {
|
|
|
277
304
|
return undefined;
|
|
278
305
|
}
|
|
279
306
|
}
|
|
307
|
+
exports.sourceMapFromCodeComment = sourceMapFromCodeComment;
|
|
280
308
|
/**
|
|
281
309
|
* Read a source map from a source map file.
|
|
282
310
|
*
|
|
@@ -286,6 +314,7 @@ function sourceMapFromMapFile(mapFilePath) {
|
|
|
286
314
|
const content = fs.readFileSync(mapFilePath, 'utf8');
|
|
287
315
|
return JSON.parse(content);
|
|
288
316
|
}
|
|
317
|
+
exports.sourceMapFromMapFile = sourceMapFromMapFile;
|
|
289
318
|
function writeToFile(filePath, fileContent) {
|
|
290
319
|
mkdirp.sync(path.dirname(filePath));
|
|
291
320
|
fs.writeFileSync(filePath, fileContent);
|
|
@@ -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
|
+
}
|
|
@@ -87,7 +87,8 @@ export declare class InstrumentationTask {
|
|
|
87
87
|
* based on the original file names the code was transpiled from.
|
|
88
88
|
*/
|
|
89
89
|
readonly originSourcePattern: OriginSourcePattern;
|
|
90
|
-
|
|
90
|
+
readonly dumpOriginsFile: string | undefined;
|
|
91
|
+
constructor(collector: CollectorSpecifier, elements: TaskElement[], originSourcePattern: OriginSourcePattern, dumpOriginsFile: string | undefined);
|
|
91
92
|
/**
|
|
92
93
|
* @returns the elements of the task.
|
|
93
94
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAK/C;;GAEG;AACH,8BAAsB,kBAAkB;CAAG;AAE3C;;;GAGG;AACH,qBAAa,WAAW;IACvB,sBAAsB;IACtB,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,2BAA2B;IAC3B,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,gEAAgE;IAChE,SAAgB,qBAAqB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;gBAExD,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,kBAAkB;IAMpF;;OAEG;IACI,SAAS,IAAI,OAAO;CAK3B;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,oEAAoE;IACpE,SAAgB,GAAG,EAAE,MAAM,CAAC;gBAEhB,SAAS,EAAE,MAAM;CAW7B;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC/B,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAEnC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS;IAKxE;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;;;;;;;;OAWG;IACI,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;IAqBpD,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAQnC,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B,OAAO,CAAC,MAAM,CAAC,+BAA+B;IAO9C,OAAO,CAAC,MAAM,CAAC,YAAY;CAM3B;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC/B;;OAEG;IACH,SAAgB,SAAS,EAAE,kBAAkB,CAAC;IAE9C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAE1C;;;OAGG;IACH,SAAgB,mBAAmB,EAAE,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAK/C;;GAEG;AACH,8BAAsB,kBAAkB;CAAG;AAE3C;;;GAGG;AACH,qBAAa,WAAW;IACvB,sBAAsB;IACtB,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,2BAA2B;IAC3B,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,gEAAgE;IAChE,SAAgB,qBAAqB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;gBAExD,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,kBAAkB;IAMpF;;OAEG;IACI,SAAS,IAAI,OAAO;CAK3B;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,oEAAoE;IACpE,SAAgB,GAAG,EAAE,MAAM,CAAC;gBAEhB,SAAS,EAAE,MAAM;CAW7B;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC/B,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAEnC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS;IAKxE;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;;;;;;;;OAWG;IACI,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;IAqBpD,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAQnC,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B,OAAO,CAAC,MAAM,CAAC,+BAA+B;IAO9C,OAAO,CAAC,MAAM,CAAC,YAAY;CAM3B;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC/B;;OAEG;IACH,SAAgB,SAAS,EAAE,kBAAkB,CAAC;IAE9C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAE1C;;;OAGG;IACH,SAAgB,mBAAmB,EAAE,mBAAmB,CAAC;IAEzD,SAAgB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;gBAGnD,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,WAAW,EAAE,EACvB,mBAAmB,EAAE,mBAAmB,EACxC,eAAe,EAAE,MAAM,GAAG,SAAS;IAQpC;;OAEG;IACH,IAAI,QAAQ,IAAI,WAAW,EAAE,CAI5B;CACD;AAED;;GAEG;AACH,qBAAa,UAAU;IACtB,iEAAiE;IACjE,SAAgB,UAAU,EAAE,MAAM,CAAC;IAEnC,oGAAoG;IACpG,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,8DAA8D;IAC9D,SAAgB,mBAAmB,EAAE,MAAM,CAAC;IAE5C,uDAAuD;IACvD,SAAgB,mBAAmB,EAAE,MAAM,CAAC;IAE5C,sDAAsD;IACtD,SAAgB,WAAW,EAAE,MAAM,CAAC;IAEpC,6DAA6D;IAC7D,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,+EAA+E;IAC/E,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAGhC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM,EAC3B,mBAAmB,EAAE,MAAM,EAC3B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;IAkBjB;;;;OAIG;IACI,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU;IAYnD;;OAEG;WACW,OAAO,IAAI,UAAU;IAInC;;;;OAIG;WACW,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,UAAU;IAKzC;;;;OAIG;WACW,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU;CAI9C;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,kBAAkB;IAC7D,2CAA2C;IAC3C,SAAgB,iBAAiB,EAAE,MAAM,CAAC;gBAE9B,iBAAiB,EAAE,MAAM;CAIrC"}
|
|
@@ -135,10 +135,11 @@ exports.OriginSourcePattern = OriginSourcePattern;
|
|
|
135
135
|
* The actual instrumentation task.
|
|
136
136
|
*/
|
|
137
137
|
class InstrumentationTask {
|
|
138
|
-
constructor(collector, elements, originSourcePattern) {
|
|
138
|
+
constructor(collector, elements, originSourcePattern, dumpOriginsFile) {
|
|
139
139
|
this.collector = commons_1.Contract.requireDefined(collector);
|
|
140
140
|
this.originSourcePattern = commons_1.Contract.requireDefined(originSourcePattern);
|
|
141
141
|
this._elements = commons_1.Contract.requireDefined(elements).slice();
|
|
142
|
+
this.dumpOriginsFile = dumpOriginsFile;
|
|
142
143
|
}
|
|
143
144
|
/**
|
|
144
145
|
* @returns the elements of the task.
|
|
@@ -9,6 +9,7 @@ export declare type ConfigurationParameters = {
|
|
|
9
9
|
collector: string;
|
|
10
10
|
include_origin?: string[];
|
|
11
11
|
exclude_origin?: string[];
|
|
12
|
+
dump_origins_to?: string;
|
|
12
13
|
};
|
|
13
14
|
/**
|
|
14
15
|
* A builder for an instrumentation task.
|
|
@@ -22,6 +23,8 @@ export declare class TaskBuilder {
|
|
|
22
23
|
private originSourceIncludePatterns;
|
|
23
24
|
/** An exclude pattern. */
|
|
24
25
|
private originSourceExcludePatterns;
|
|
26
|
+
/** File path where all origins from the source map should be dumped in json format, or undefined if no origins should be dumped */
|
|
27
|
+
private dumpOriginsFile;
|
|
25
28
|
constructor();
|
|
26
29
|
/** Set the collector by extracting the information from a given string */
|
|
27
30
|
setCollectorFromString(collectorSpecification: string): this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskBuilder.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/TaskBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,mBAAmB,EAGnB,kBAAkB,EAElB,MAAM,QAAQ,CAAC;AAMhB,2DAA2D;AAC3D,oBAAY,uBAAuB,GAAG;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAElB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"TaskBuilder.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/TaskBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,mBAAmB,EAGnB,kBAAkB,EAElB,MAAM,QAAQ,CAAC;AAMhB,2DAA2D;AAC3D,oBAAY,uBAAuB,GAAG;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAElB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAkBF;;GAEG;AACH,qBAAa,WAAW;IACvB,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC,6CAA6C;IAC7C,OAAO,CAAC,SAAS,CAA4B;IAE7C,0BAA0B;IAC1B,OAAO,CAAC,2BAA2B,CAAuB;IAE1D,0BAA0B;IAC1B,OAAO,CAAC,2BAA2B,CAAuB;IAE1D,mIAAmI;IACnI,OAAO,CAAC,eAAe,CAAqB;;IAO5C,0EAA0E;IAC1E,sBAAsB,CAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI;IAM5D,yGAAyG;IACzG,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI;IAKpE,2GAA2G;IAC3G,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI;IAKpE,yBAAyB;IACzB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,kBAAkB,GAAG,IAAI;IAKlG;;;;OAIG;IACH,aAAa,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAmCpD;;;;;;;OAOG;IACH,OAAO,CAAC,4CAA4C;IAgCpD;;;;;;OAMG;IACH,OAAO,CAAC,0BAA0B;IASlC;;OAEG;IACI,KAAK,IAAI,mBAAmB;CASnC"}
|
|
@@ -84,6 +84,7 @@ class TaskBuilder {
|
|
|
84
84
|
const inPlace = (_b = config.in_place) !== null && _b !== void 0 ? _b : true;
|
|
85
85
|
const target = config.to;
|
|
86
86
|
const sourceMap = config.source_map;
|
|
87
|
+
this.dumpOriginsFile = config.dump_origins_to;
|
|
87
88
|
this.setCollectorFromString(config.collector);
|
|
88
89
|
this.setOriginSourceIncludePatterns(config.include_origin);
|
|
89
90
|
this.setOriginSourceExcludePatterns(config.exclude_origin);
|
|
@@ -164,7 +165,7 @@ class TaskBuilder {
|
|
|
164
165
|
*/
|
|
165
166
|
build() {
|
|
166
167
|
const pattern = new Task_1.OriginSourcePattern(this.originSourceIncludePatterns, this.originSourceExcludePatterns);
|
|
167
|
-
return new Task_1.InstrumentationTask(commons_1.Contract.requireDefined(this.collector), this.elements, pattern);
|
|
168
|
+
return new Task_1.InstrumentationTask(commons_1.Contract.requireDefined(this.collector), this.elements, pattern, this.dumpOriginsFile);
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
exports.TaskBuilder = TaskBuilder;
|
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",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"build": "tsc --project tsconfig.json && yarn buildVaccine",
|
|
18
18
|
"buildVaccine": "node esbuild.mjs",
|
|
19
19
|
"instrumenter": "node dist/src/main.js",
|
|
20
|
-
"test": "yarn build && NODE_OPTIONS='--experimental-vm-modules' jest --forceExit --coverage --silent=true --detectOpenHandles"
|
|
20
|
+
"test": "yarn build && NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=8192' jest --forceExit --coverage --silent=true --detectOpenHandles"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist/**/*"
|
|
24
24
|
],
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@babel/core": "^7.
|
|
27
|
-
"@babel/plugin-transform-modules-commonjs": "^7.
|
|
28
|
-
"@babel/preset-env": "^7.
|
|
26
|
+
"@babel/core": "^7.18.5",
|
|
27
|
+
"@babel/plugin-transform-modules-commonjs": "^7.18.2",
|
|
28
|
+
"@babel/preset-env": "^7.18.2",
|
|
29
29
|
"@types/async": "^3.2.6",
|
|
30
30
|
"@types/bunyan": "^1.8.8",
|
|
31
31
|
"@types/convert-source-map": "^1.5.1",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"typescript": "^4.4.3"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@babel/generator": "^7.
|
|
51
|
-
"@babel/parser": "^7.
|
|
52
|
-
"@babel/traverse": "^7.
|
|
53
|
-
"@babel/types": "^7.
|
|
50
|
+
"@babel/generator": "^7.18.2",
|
|
51
|
+
"@babel/parser": "^7.18.5",
|
|
52
|
+
"@babel/traverse": "^7.18.6",
|
|
53
|
+
"@babel/types": "^7.18.4",
|
|
54
54
|
"@cqse/commons": "^0.0.1-beta.1",
|
|
55
55
|
"@types/micromatch": "^4.0.2",
|
|
56
56
|
"argparse": "^2.0.1",
|
|
@@ -59,10 +59,9 @@
|
|
|
59
59
|
"convert-source-map": "^1.7.0",
|
|
60
60
|
"foreground-child": "^2.0.0",
|
|
61
61
|
"glob": "^7.1.7",
|
|
62
|
-
"istanbul-lib-instrument": "^
|
|
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;AAEtB;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,GAClD,MAAM,CAYR"}
|
|
@@ -1,82 +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
|
-
/**
|
|
12
|
-
* Remove IstanbulJs instrumentations based on the given
|
|
13
|
-
* hook `makeCoverable`.
|
|
14
|
-
*
|
|
15
|
-
* An instrumentation is removed if the hook `makeCoverable` returns `false`.
|
|
16
|
-
*/
|
|
17
|
-
function cleanSourceCode(code, esModules, makeCoverable) {
|
|
18
|
-
const ast = (0, parser_1.parse)(code, { sourceType: esModules ? 'module' : 'script' });
|
|
19
|
-
(0, traverse_1.default)(ast, {
|
|
20
|
-
UpdateExpression(path) {
|
|
21
|
-
if (isCoverageIncrementNode(path)) {
|
|
22
|
-
if (path.node.loc && !makeCoverable(path.node.loc)) {
|
|
23
|
-
path.remove();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
return (0, generator_1.default)(ast, {}, code).code;
|
|
29
|
-
}
|
|
30
|
-
exports.cleanSourceCode = cleanSourceCode;
|
|
31
|
-
/**
|
|
32
|
-
* Checks if the given `path.node` to a statement like `cov_104fq7oo4i().f[0]++;`
|
|
33
|
-
*/
|
|
34
|
-
function isCoverageIncrementNode(path) {
|
|
35
|
-
return extractCoverageCallExpression(path.node) !== undefined;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Returns the call expression from `cov_2pvvu1hl8v().b[2][0]++;` if
|
|
39
|
-
* the given UpdateExpression is a branch coverage update expression.
|
|
40
|
-
*/
|
|
41
|
-
function extractBranchCounterExpression(expr) {
|
|
42
|
-
if (expr.operator === '++' &&
|
|
43
|
-
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
44
|
-
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
45
|
-
(0, types_1.isMemberExpression)(expr.argument.object.object) &&
|
|
46
|
-
(0, types_1.isCallExpression)(expr.argument.object.object.object)) {
|
|
47
|
-
// Branch counter
|
|
48
|
-
return extractCoverageObjectCall(expr.argument.object.object.object);
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Returns the call expression from `cov_104fq7oo4i().f[0]++;` if
|
|
54
|
-
* the given UpdateExpression is a function or statement coverage update expression.
|
|
55
|
-
*/
|
|
56
|
-
function extractFunctionOrStatementCounterExpression(expr) {
|
|
57
|
-
if (expr.operator === '++' &&
|
|
58
|
-
(0, types_1.isMemberExpression)(expr.argument) &&
|
|
59
|
-
(0, types_1.isMemberExpression)(expr.argument.object) &&
|
|
60
|
-
(0, types_1.isCallExpression)(expr.argument.object.object)) {
|
|
61
|
-
// Function and statement counter
|
|
62
|
-
return extractCoverageObjectCall(expr.argument.object.object);
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Given an `UpdateExpression` extract the call expression returning the coverage object.
|
|
68
|
-
*/
|
|
69
|
-
function extractCoverageCallExpression(expr) {
|
|
70
|
-
var _a;
|
|
71
|
-
return (_a = extractBranchCounterExpression(expr)) !== null && _a !== void 0 ? _a : extractFunctionOrStatementCounterExpression(expr);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Check if the given call expression is a coverage call expression.
|
|
75
|
-
* If this is not the case return `undefined`, and the call expression itself otherwise.
|
|
76
|
-
*/
|
|
77
|
-
function extractCoverageObjectCall(callExpression) {
|
|
78
|
-
if (callExpression && (0, types_1.isIdentifier)(callExpression.callee) && callExpression.callee.name.startsWith('cov_')) {
|
|
79
|
-
return callExpression;
|
|
80
|
-
}
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|