@teamscale/javascript-instrumenter 0.0.1-beta.3 → 0.0.1-beta.33

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
@@ -9,7 +9,7 @@ in the Teamscale Simple Coverage Format and sent to a Teamscale instance.
9
9
  The Teamscale JavaScript Profiler consists of the [Coverage Collector](https://www.npmjs.com/package/@teamscale/coverage-collector)
10
10
  and this JavaScript Instrumenter.
11
11
  More details on using them (in combination) can be found
12
- on the projects' [Github page](https://github.com/cqse/teamscale-javascript-profiler/).
12
+ in the [Teamscale Documentation](https://docs.teamscale.com/howto/recording-test-coverage-for-javascript/).
13
13
 
14
14
  The Instrumenter instruments a given (set of) JavaScript file(s) such that (1) coverage
15
15
  information is produced and (2) forwarded to the Collector.
@@ -25,34 +25,12 @@ yarn install
25
25
  yarn build
26
26
  ```
27
27
 
28
- ### Preparation: Source Maps
29
-
30
- Please make sure to enable the generation of source map files to ensure
31
- that the profiled code can be mapped back to the original.
32
-
33
- For example, when the tool Vite is used to bundle the code,
34
- the generation of source map information can be enabled by setting:
35
-
36
- ```
37
- build: {
38
- sourcemap: true
39
- }
40
- ```
41
-
42
- or
43
-
44
- ```
45
- build: {
46
- sourcemap: `inline`
47
- }
48
- ```
49
-
50
28
  ## Workflow Integration
51
29
 
52
30
  There are several options to run the Instrumenter. For example, via `yarn` by running
53
31
 
54
32
  ```
55
- yarn instrument
33
+ yarn instrumenter
56
34
  ```
57
35
 
58
36
  or via `npx` by running
@@ -74,30 +52,19 @@ the file must contain source-map information, or the source-map file
74
52
  must be placed along with the source file in the same directory.
75
53
 
76
54
  ```
77
- yarn run --inplace ./the/path/to/the/file.js
55
+ yarn instrumenter --inplace ./the/path/to/the/file.js
78
56
  ```
79
57
 
80
58
  ```
81
- yarn run --inplace ./the/path/to/the/file.js --source-map ./the/path/to/the/source.map
59
+ yarn instrumenter --inplace ./the/path/to/the/file.js --source-map ./the/path/to/the/source.map
82
60
  ```
83
61
 
84
62
  ```
85
- yarn run ./the/path/to/the/file.js --to ./the/file/path/to/write/to.js
63
+ yarn instrumenter ./the/path/to/the/file.js --to ./the/file/path/to/write/to.js
86
64
  ```
87
65
 
88
- ### Instrumenting all JavaScript Files in a Folder
89
-
90
- We think that dealing with sets of files, in particular including or excluding
91
- files that match particular file masks should be done by other tools.
92
- In a UNIX environment, you should consider using `find` with corresponding
93
- filters and an `-exec` argument to run the instrumenter.
94
-
95
- ### Integration with Testing Frameworks
96
-
97
- This is planned work: Provide a Babel plugin that provides a code transformation
98
- such that coverage information is collected and this information is forwarded.
99
-
100
66
  ## Limitations
101
67
 
102
68
  This tool inherits most of the limitations of IstanbulJs, including
103
69
  a considerable performance impact.
70
+
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamscale/javascript-instrumenter",
3
- "version": "0.0.1-beta.3",
3
+ "version": "0.0.1-beta.33",
4
4
  "description": "Istanbul-based coverage instrumenter with coverage forwarding via WebSockets",
5
5
  "main": "dist/src/main.js",
6
6
  "bin": "dist/src/main.js",
@@ -12,24 +12,27 @@
12
12
  "url": "https://github.com/cqse/teamscale-javascript-profiler.git"
13
13
  },
14
14
  "scripts": {
15
+ "prepublishOnly": "yarn clean && yarn build",
15
16
  "clean": "rimraf dist tsconfig.tsbuildinfo",
16
- "build": "tsc --project tsconfig.json && node esbuild.mjs",
17
- "instrument": "node dist/src/main.js",
17
+ "build": "tsc --project tsconfig.json && yarn buildVaccine",
18
+ "buildVaccine": "node esbuild.mjs",
19
+ "instrumenter": "node dist/src/main.js",
18
20
  "test": "yarn build && NODE_OPTIONS='--experimental-vm-modules' jest --forceExit --coverage --silent=true --detectOpenHandles"
19
21
  },
20
22
  "files": [
21
23
  "dist/**/*"
22
24
  ],
23
25
  "devDependencies": {
24
- "@babel/core": "^7.14.0",
26
+ "@babel/core": "^7.17.5",
25
27
  "@babel/plugin-transform-modules-commonjs": "^7.15.4",
26
28
  "@babel/preset-env": "^7.14.1",
27
29
  "@types/async": "^3.2.6",
30
+ "@types/bunyan": "^1.8.8",
28
31
  "@types/convert-source-map": "^1.5.1",
29
32
  "@types/glob": "^7.1.3",
30
33
  "@types/istanbul-lib-instrument": "^1.7.4",
31
34
  "@types/jest": "^27.0.1",
32
- "@types/mkdirp": "^1.0.1",
35
+ "@types/mkdirp": "^1.0.2",
33
36
  "@types/node": "^15.0.1",
34
37
  "@types/source-map": "^0.5.7",
35
38
  "@types/ws": "^7.4.4",
@@ -44,10 +47,15 @@
44
47
  "typescript": "^4.4.3"
45
48
  },
46
49
  "dependencies": {
50
+ "@babel/generator": "^7.17.3",
51
+ "@babel/parser": "^7.17.3",
52
+ "@babel/traverse": "^7.17.3",
53
+ "@babel/types": "^7.17.0",
47
54
  "@cqse/commons": "^0.0.1-beta.1",
48
55
  "@types/micromatch": "^4.0.2",
49
56
  "argparse": "^2.0.1",
50
- "async": "^3.2.0",
57
+ "async": "^3.2.3",
58
+ "bunyan": "^1.8.15",
51
59
  "convert-source-map": "^1.7.0",
52
60
  "foreground-child": "^2.0.0",
53
61
  "glob": "^7.1.7",
@@ -55,11 +63,10 @@
55
63
  "micromatch": "4.0.4",
56
64
  "mkdirp": "^1.0.4",
57
65
  "nyc": "^15.1.0",
58
- "source-map": "0.6.1",
66
+ "source-map": "0.7.3",
59
67
  "typescript-optional": "^2.0.1",
60
68
  "unload": "^2.2.0",
61
- "web-worker": "^1.0.0",
62
- "winston": "^3.3.3"
69
+ "web-worker": "^1.0.0"
63
70
  },
64
71
  "publishConfig": {
65
72
  "access": "public"
package/dist/src/App.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { TaskResult } from './instrumenter/Task';
2
2
  import { ConfigurationParameters } from './instrumenter/TaskBuilder';
3
- import { Logger } from 'winston';
3
+ import Logger from "bunyan";
4
4
  /**
5
5
  * Entry points of the instrumenter, including command line argument parsing.
6
6
  */
@@ -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;AAGlF,OAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAG1C;;GAEG;AACH,qBAAa,GAAG;IACf;;;OAGG;WACiB,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;IAa9C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAgC1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAa1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAS/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"}
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;IAgC1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IA0B1B;;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
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -29,8 +33,9 @@ const commons_1 = require("@cqse/commons");
29
33
  const TaskBuilder_1 = require("./instrumenter/TaskBuilder");
30
34
  const path = __importStar(require("path"));
31
35
  const package_json_1 = require("../package.json");
32
- const winston_1 = __importDefault(require("winston"));
33
36
  const fs_1 = require("fs");
37
+ const mkdirp_1 = __importDefault(require("mkdirp"));
38
+ const bunyan_1 = __importDefault(require("bunyan"));
34
39
  /**
35
40
  * Entry points of the instrumenter, including command line argument parsing.
36
41
  */
@@ -59,11 +64,11 @@ class App {
59
64
  parser.add_argument('-v', '--version', { action: 'version', version: package_json_1.version });
60
65
  parser.add_argument('-i', '--in-place', {
61
66
  action: 'store_true',
62
- help: 'If set, the original files to instrument are replaced (!!) by their instrumented counterparts.'
67
+ help: 'If set, the original files to instrument are replaced by their instrumented counterparts.'
63
68
  });
64
69
  parser.add_argument('-d', '--debug', { action: 'store_true' });
65
70
  parser.add_argument('-o', '--to', {
66
- help: 'Name of the file to write the instrumented version to.'
71
+ help: 'Path (directory or file name) to write the instrumented version to.'
67
72
  });
68
73
  parser.add_argument('-s', '--source-map', {
69
74
  help: 'External location of source-map files to consider.'
@@ -85,27 +90,31 @@ class App {
85
90
  * Construct the logger.
86
91
  */
87
92
  static buildLogger(config) {
88
- return winston_1.default.createLogger({
89
- level: config.debug ? 'debug' : 'info',
90
- format: winston_1.default.format.json(),
91
- defaultMeta: {},
92
- transports: [
93
- new winston_1.default.transports.File({ filename: 'logs/instrumenter-error.log', level: 'error' }),
94
- new winston_1.default.transports.File({ filename: 'logs/instrumenter-combined.log' }),
95
- new winston_1.default.transports.Console({ format: winston_1.default.format.simple(), level: 'info' })
96
- ]
97
- });
93
+ const logfilePath = 'logs/instrumenter.log';
94
+ mkdirp_1.default.sync(path.dirname(logfilePath));
95
+ const logLevel = config.debug ? 'debug' : 'error';
96
+ return bunyan_1.default.createLogger({ name: "Instrumenter",
97
+ streams: [
98
+ {
99
+ level: logLevel,
100
+ stream: {
101
+ write: (rec) => {
102
+ console.log('[%s] %s: %s', rec.time.toISOString(), bunyan_1.default.nameFromLevel[rec.level], rec.msg);
103
+ }
104
+ },
105
+ type: 'raw'
106
+ },
107
+ {
108
+ level: logLevel,
109
+ path: logfilePath
110
+ }
111
+ ] });
98
112
  }
99
113
  /**
100
114
  * A logger for testing.
101
115
  */
102
116
  static buildDummyLogger() {
103
- return winston_1.default.createLogger({
104
- level: 'info',
105
- format: winston_1.default.format.json(),
106
- defaultMeta: {},
107
- transports: [new winston_1.default.transports.Console({ format: winston_1.default.format.simple(), level: 'info' })]
108
- });
117
+ return bunyan_1.default.createLogger({ name: "Instrumenter" });
109
118
  }
110
119
  /**
111
120
  * The instrumenter can also be started by providing the configuration dictionary explicitly.
@@ -0,0 +1,9 @@
1
+ import { SourceLocation } from '@babel/types';
2
+ /**
3
+ * Remove IstanbulJs instrumentations based on the given
4
+ * hook `makeCoverable`.
5
+ *
6
+ * An instrumentation is removed if the hook `makeCoverable` returns `false`.
7
+ */
8
+ export declare function cleanSourceCode(code: string, esModules: boolean, makeCoverable: (location: SourceLocation) => boolean): string;
9
+ //# sourceMappingURL=Cleaner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cleaner.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Cleaner.ts"],"names":[],"mappings":"AAGA,OAAO,EAON,cAAc,EACd,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,CAcR"}
@@ -0,0 +1,102 @@
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
+ ExpressionStatement(path) {
21
+ if (isUnsupportedCounterTypeIncrement(path)) {
22
+ path.remove();
23
+ }
24
+ else if (isCoverageIncrementNode(path)) {
25
+ if (path.node.loc && !makeCoverable(path.node.loc)) {
26
+ path.remove();
27
+ }
28
+ }
29
+ }
30
+ });
31
+ return (0, generator_1.default)(ast, {}, code).code;
32
+ }
33
+ exports.cleanSourceCode = cleanSourceCode;
34
+ /**
35
+ * Checks if the given `path.node` to a statement like `cov_104fq7oo4i().f[0]++;`
36
+ */
37
+ function isCoverageIncrementNode(path) {
38
+ const expr = path.node.expression;
39
+ if (!(0, types_1.isUpdateExpression)(expr)) {
40
+ return false;
41
+ }
42
+ return extractCoverageCallExpression(expr) !== undefined;
43
+ }
44
+ /**
45
+ * Is the given expression statement a coverage increment that
46
+ * is not supported by our approach?
47
+ *
48
+ * For example, branch coverage is not supported.
49
+ */
50
+ function isUnsupportedCounterTypeIncrement(path) {
51
+ if (!(0, types_1.isUpdateExpression)(path.node.expression)) {
52
+ return false;
53
+ }
54
+ return extractBranchCounterExpression(path.node.expression) !== undefined;
55
+ }
56
+ /**
57
+ * Returns the call expression from `cov_2pvvu1hl8v().b[2][0]++;` if
58
+ * the given UpdateExpression is a branch coverage update expression.
59
+ */
60
+ function extractBranchCounterExpression(expr) {
61
+ if (expr.operator === '++' &&
62
+ (0, types_1.isMemberExpression)(expr.argument) &&
63
+ (0, types_1.isMemberExpression)(expr.argument.object) &&
64
+ (0, types_1.isMemberExpression)(expr.argument.object.object) &&
65
+ (0, types_1.isCallExpression)(expr.argument.object.object.object)) {
66
+ // Branch counter
67
+ return extractCoverageObjectCall(expr.argument.object.object.object);
68
+ }
69
+ return undefined;
70
+ }
71
+ /**
72
+ * Returns the call expression from `cov_104fq7oo4i().f[0]++;` if
73
+ * the given UpdateExpression is a function or statement coverage update expression.
74
+ */
75
+ function extractFunctionOrStatementCounterExpression(expr) {
76
+ if (expr.operator === '++' &&
77
+ (0, types_1.isMemberExpression)(expr.argument) &&
78
+ (0, types_1.isMemberExpression)(expr.argument.object) &&
79
+ (0, types_1.isCallExpression)(expr.argument.object.object)) {
80
+ // Function and statement counter
81
+ return extractCoverageObjectCall(expr.argument.object.object);
82
+ }
83
+ return undefined;
84
+ }
85
+ /**
86
+ * Given an `UpdateExpression` extract the call expression returning the coverage object.
87
+ */
88
+ function extractCoverageCallExpression(expr) {
89
+ var _a;
90
+ return (_a = extractBranchCounterExpression(expr)) !== null && _a !== void 0 ? _a : extractFunctionOrStatementCounterExpression(expr);
91
+ }
92
+ /**
93
+ * Check if the given call expression is a coverage call expression.
94
+ * If this is not the case return `undefined`, and the call expression itself otherwise.
95
+ */
96
+ function extractCoverageObjectCall(callExpression) {
97
+ if (callExpression && (0, types_1.isIdentifier)(callExpression.callee)
98
+ && callExpression.callee.name.startsWith('cov_')) {
99
+ return callExpression;
100
+ }
101
+ return undefined;
102
+ }
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -1,5 +1,5 @@
1
1
  import { CollectorSpecifier, InstrumentationTask, OriginSourcePattern, TaskElement, TaskResult } from './Task';
2
- import { Logger } from 'winston';
2
+ import Logger from "bunyan";
3
3
  export declare const IS_INSTRUMENTED_TOKEN = "/** $IS_JS_PROFILER_INSTRUMENTED=true **/";
4
4
  /**
5
5
  * An instrumenter that can conduct a {@code InstrumentationTask}.
@@ -38,7 +38,9 @@ export declare class IstanbulInstrumenter implements IInstrumenter {
38
38
  * @param taskElement - The task element to perform the instrumentation for.
39
39
  * @param sourcePattern - A pattern to restrict the instrumentation to only a fraction of the task element.
40
40
  */
41
- instrumentOne(collector: CollectorSpecifier, taskElement: TaskElement, sourcePattern: OriginSourcePattern): TaskResult;
41
+ instrumentOne(collector: CollectorSpecifier, taskElement: TaskElement, sourcePattern: OriginSourcePattern): Promise<TaskResult>;
42
+ private removeUnwantedInstrumentation;
43
+ private loadSourceMap;
42
44
  /**
43
45
  * Loads the vaccine from the vaccine file and adjusts some template parameters.
44
46
  *
@@ -64,10 +66,11 @@ export declare class IstanbulInstrumenter implements IInstrumenter {
64
66
  */
65
67
  private configurationAlternativesFor;
66
68
  /**
67
- * Given a source code file and the task element, load the corresponding sourcemap.
69
+ * Given a source code file, load the corresponding sourcemap.
68
70
  *
69
71
  * @param inputSource - The source code that might contain sourcemap comments.
70
- * @param taskElement - The task element that can have a reference to an external sourcemap.
72
+ * @param taskFile - The name of the file the `inputSource` is from.
73
+ * @param externalSourceMapFile - An external source map file to consider.
71
74
  */
72
75
  private loadInputSourceMap;
73
76
  }
@@ -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,WAAW,EACX,UAAU,EACV,MAAM,QAAQ,CAAC;AAOhB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,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;IACH,UAAU,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAW1D;;;;;;OAMG;IACH,aAAa,CACZ,SAAS,EAAE,kBAAkB,EAC7B,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,mBAAmB,GAChC,UAAU;IAuFb;;;;OAIG;IACH,OAAO,CAAC,WAAW;IASnB;;;;;;;OAOG;IACH,OAAO,CAAC,gCAAgC;IAQxC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAcpC;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;CAW1B"}
1
+ {"version":3,"file":"Instrumenter.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Instrumenter.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EAGnB,WAAW,EACX,UAAU,EACV,MAAM,QAAQ,CAAC;AAUhB,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;IAYhE;;;;;;OAMG;IACG,aAAa,CAClB,SAAS,EAAE,kBAAkB,EAC7B,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,mBAAmB,GAChC,OAAO,CAAC,UAAU,CAAC;YAoGR,6BAA6B;YAkC7B,aAAa;IAe3B;;;;OAIG;IACH,OAAO,CAAC,WAAW;IASnB;;;;;;;OAOG;IACH,OAAO,CAAC,gCAAgC;IAQxC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAcpC;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;CAe1B"}
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -18,14 +22,22 @@ var __importStar = (this && this.__importStar) || function (mod) {
18
22
  __setModuleDefault(result, mod);
19
23
  return result;
20
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
21
28
  Object.defineProperty(exports, "__esModule", { value: true });
22
29
  exports.IstanbulInstrumenter = exports.IS_INSTRUMENTED_TOKEN = void 0;
23
30
  const Task_1 = require("./Task");
24
31
  const commons_1 = require("@cqse/commons");
32
+ const source_map_1 = require("source-map");
25
33
  const istanbul = __importStar(require("istanbul-lib-instrument"));
26
34
  const fs = __importStar(require("fs"));
35
+ const mkdirp = __importStar(require("mkdirp"));
27
36
  const path = __importStar(require("path"));
28
37
  const convertSourceMap = __importStar(require("convert-source-map"));
38
+ const Cleaner_1 = require("./Cleaner");
39
+ const typescript_optional_1 = require("typescript-optional");
40
+ const async_1 = __importDefault(require("async"));
29
41
  exports.IS_INSTRUMENTED_TOKEN = '/** $IS_JS_PROFILER_INSTRUMENTED=true **/';
30
42
  /**
31
43
  * An instrumenter based on the IstanbulJs instrumentation and coverage framework.
@@ -39,14 +51,16 @@ class IstanbulInstrumenter {
39
51
  /**
40
52
  * {@inheritDoc #IInstrumenter.instrument}
41
53
  */
42
- instrument(task) {
43
- fs.existsSync(this.vaccineFilePath);
44
- // ATTENTION: Here is potential for parallelization. Maybe we can
45
- // run several instrumentation workers in parallel?
46
- const result = task.elements
47
- .map(e => this.instrumentOne(task.collector, e, task.originSourcePattern))
48
- .reduce((prev, current) => current.withIncrement(prev), Task_1.TaskResult.neutral());
49
- return Promise.resolve(result);
54
+ async instrument(task) {
55
+ // We limit the number of instrumentations in parallel to one to
56
+ // not overuse memory (NodeJS has only limited mem to use).
57
+ return async_1.default.mapLimit(task.elements, 1, async (taskElement) => {
58
+ return await this.instrumentOne(task.collector, taskElement, task.originSourcePattern);
59
+ }).then((results) => {
60
+ return results.reduce((prev, curr) => {
61
+ return prev.withIncrement(curr);
62
+ }, Task_1.TaskResult.neutral());
63
+ });
50
64
  }
51
65
  /**
52
66
  * Perform the instrumentation for one given task element (file to instrument).
@@ -55,19 +69,19 @@ class IstanbulInstrumenter {
55
69
  * @param taskElement - The task element to perform the instrumentation for.
56
70
  * @param sourcePattern - A pattern to restrict the instrumentation to only a fraction of the task element.
57
71
  */
58
- instrumentOne(collector, taskElement, sourcePattern) {
59
- var _a, _b, _c;
72
+ async instrumentOne(collector, taskElement, sourcePattern) {
73
+ var _a, _b;
60
74
  const inputFileSource = fs.readFileSync(taskElement.fromFile, 'utf8');
61
75
  // We skip files that we have already instrumented
62
76
  if (inputFileSource.startsWith(exports.IS_INSTRUMENTED_TOKEN)) {
63
77
  if (!taskElement.isInPlace()) {
64
- fs.writeFileSync(taskElement.toFile, inputFileSource);
78
+ writeToFile(taskElement.toFile, inputFileSource);
65
79
  }
66
- return new Task_1.TaskResult(0, 0, 1, 0, 0, 0);
80
+ return new Task_1.TaskResult(0, 0, 0, 1, 0, 0, 0);
67
81
  }
68
82
  // Not all file types are supported by the instrumenter
69
83
  if (!this.isFileTypeSupported(taskElement.fromFile)) {
70
- return new Task_1.TaskResult(0, 0, 0, 1, 0, 0);
84
+ return new Task_1.TaskResult(0, 0, 0, 0, 1, 0, 0);
71
85
  }
72
86
  // Report progress
73
87
  this.logger.info(`Instrumenting "${path.basename(taskElement.fromFile)}"`);
@@ -77,29 +91,37 @@ class IstanbulInstrumenter {
77
91
  // alternative configurations of the instrumenter.
78
92
  const configurationAlternatives = this.configurationAlternativesFor(taskElement);
79
93
  for (let i = 0; i < configurationAlternatives.length; i++) {
94
+ const configurationAlternative = configurationAlternatives[i];
80
95
  let inputSourceMap;
81
96
  try {
82
- const instrumenter = istanbul.createInstrumenter(configurationAlternatives[i]);
83
- inputSourceMap = this.loadInputSourceMap(inputFileSource, taskElement);
97
+ const instrumenter = istanbul.createInstrumenter(configurationAlternative);
98
+ inputSourceMap = this.loadInputSourceMap(inputFileSource, taskElement.fromFile, taskElement.externalSourceMapFile);
84
99
  // Based on the source maps of the file to instrument, we can now
85
100
  // decide if we should NOT write an instrumented version of it
86
101
  // and use the original code instead and write it to the target path.
87
102
  //
88
- if (this.shouldExcludeFromInstrumentation(sourcePattern, taskElement.fromFile, (_b = (_a = instrumenter.lastSourceMap()) === null || _a === void 0 ? void 0 : _a.sources) !== null && _b !== void 0 ? _b : [])) {
89
- fs.writeFileSync(taskElement.toFile, inputFileSource);
90
- return new Task_1.TaskResult(1, 0, 0, 0, 0, 0);
103
+ const originSourceFiles = (_a = inputSourceMap === null || inputSourceMap === void 0 ? void 0 : inputSourceMap.sources) !== null && _a !== void 0 ? _a : [];
104
+ if (this.shouldExcludeFromInstrumentation(sourcePattern, taskElement.fromFile, originSourceFiles)) {
105
+ writeToFile(taskElement.toFile, inputFileSource);
106
+ return new Task_1.TaskResult(0, 1, 0, 0, 0, 0, 0);
91
107
  }
92
108
  // The main instrumentation (adding coverage statements) is performed now:
93
109
  instrumentedSource = instrumenter
94
110
  .instrumentSync(inputFileSource, taskElement.fromFile, inputSourceMap)
95
111
  .replace(/return actualCoverage/g, 'return makeCoverageInterceptor(actualCoverage)')
96
112
  .replace(/new Function\("return this"\)\(\)/g, "typeof window === 'object' ? window : this");
97
- this.logger.debug('Instrumentation source maps to:', (_c = instrumenter.lastSourceMap()) === null || _c === void 0 ? void 0 : _c.sources);
113
+ this.logger.debug('Instrumentation source maps to:', (_b = instrumenter.lastSourceMap()) === null || _b === void 0 ? void 0 : _b.sources);
114
+ // In case of a bundle, the initial instrumentation step might have added
115
+ // too much and undesired instrumentations. Remove them now.
116
+ instrumentedSource = await this.removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern);
98
117
  // The process also can result in a new source map that we will append in the result.
99
118
  //
100
119
  // `lastSourceMap` === Sourcemap for the last file that was instrumented.
101
120
  finalSourceMap = convertSourceMap.fromObject(instrumenter.lastSourceMap()).toComment();
102
- break;
121
+ // We now can glue together the final version of the instrumented file.
122
+ const vaccineSource = this.loadVaccine(collector);
123
+ writeToFile(taskElement.toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${vaccineSource} ${instrumentedSource} \n${finalSourceMap}`);
124
+ return new Task_1.TaskResult(1, 0, 0, 0, 0, 0, 0);
103
125
  }
104
126
  catch (e) {
105
127
  // If also the last configuration alternative failed,
@@ -108,16 +130,41 @@ class IstanbulInstrumenter {
108
130
  if (!inputSourceMap) {
109
131
  return Task_1.TaskResult.warning(`Failed loading input source map for ${taskElement.fromFile}: ${e.message}`);
110
132
  }
111
- fs.writeFileSync(taskElement.toFile, inputFileSource);
133
+ writeToFile(taskElement.toFile, inputFileSource);
112
134
  return Task_1.TaskResult.error(e);
113
135
  }
114
136
  }
115
137
  }
116
- // We now can glue together the final version of the instrumented file.
117
- //
118
- const vaccineSource = this.loadVaccine(collector);
119
- fs.writeFileSync(taskElement.toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${vaccineSource} ${instrumentedSource} \n${finalSourceMap}`);
120
- return new Task_1.TaskResult(1, 0, 0, 0, 0, 0);
138
+ return new Task_1.TaskResult(0, 0, 0, 0, 0, 1, 0);
139
+ }
140
+ async removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern) {
141
+ // Read the source map from the instrumented file
142
+ const instrumentedSourceMapConsumer = await this.loadSourceMap(instrumentedSource, taskElement.fromFile);
143
+ // Without a source map, excludes/includes do not work.
144
+ if (!instrumentedSourceMapConsumer) {
145
+ return instrumentedSource;
146
+ }
147
+ // Remove the unwanted instrumentation
148
+ const cleaned = (0, Cleaner_1.cleanSourceCode)(instrumentedSource, configurationAlternative.esModules, location => {
149
+ const originalPosition = instrumentedSourceMapConsumer.originalPositionFor({
150
+ line: location.start.line,
151
+ column: location.start.column
152
+ });
153
+ if (!originalPosition.source) {
154
+ return true;
155
+ }
156
+ return sourcePattern.isAnyIncluded([originalPosition.source]);
157
+ });
158
+ // Explicitly free the source map to avoid memory leaks
159
+ instrumentedSourceMapConsumer.destroy();
160
+ return cleaned;
161
+ }
162
+ async loadSourceMap(instrumentedSource, instrumentedSourceFileName) {
163
+ const instrumentedSourceMap = this.loadInputSourceMap(instrumentedSource, instrumentedSourceFileName, typescript_optional_1.Optional.empty());
164
+ if (instrumentedSourceMap) {
165
+ return await new source_map_1.SourceMapConsumer(instrumentedSourceMap);
166
+ }
167
+ return undefined;
121
168
  }
122
169
  /**
123
170
  * Loads the vaccine from the vaccine file and adjusts some template parameters.
@@ -125,7 +172,7 @@ class IstanbulInstrumenter {
125
172
  * @param collector - The collector to send coverage information to.
126
173
  */
127
174
  loadVaccine(collector) {
128
- // We first replace some of the parameters in the file with the
175
+ // We first replace parameters in the file with the
129
176
  // actual values, for example, the collector to send the coverage information to.
130
177
  return fs
131
178
  .readFileSync(this.vaccineFilePath, 'utf8')
@@ -166,21 +213,22 @@ class IstanbulInstrumenter {
166
213
  ];
167
214
  }
168
215
  /**
169
- * Given a source code file and the task element, load the corresponding sourcemap.
216
+ * Given a source code file, load the corresponding sourcemap.
170
217
  *
171
218
  * @param inputSource - The source code that might contain sourcemap comments.
172
- * @param taskElement - The task element that can have a reference to an external sourcemap.
219
+ * @param taskFile - The name of the file the `inputSource` is from.
220
+ * @param externalSourceMapFile - An external source map file to consider.
173
221
  */
174
- loadInputSourceMap(inputSource, taskElement) {
175
- if (taskElement.externalSourceMapFile.isPresent()) {
176
- const sourceMapOrigin = taskElement.externalSourceMapFile.get();
222
+ loadInputSourceMap(inputSourceCode, taskFile, externalSourceMapFile) {
223
+ if (externalSourceMapFile.isPresent()) {
224
+ const sourceMapOrigin = externalSourceMapFile.get();
177
225
  if (!(sourceMapOrigin instanceof Task_1.SourceMapFileReference)) {
178
226
  throw new commons_1.IllegalArgumentException('Type of source map not yet supported!');
179
227
  }
180
228
  return sourceMapFromMapFile(sourceMapOrigin.sourceMapFilePath);
181
229
  }
182
230
  else {
183
- return sourceMapFromCodeComment(inputSource, taskElement.fromFile);
231
+ return sourceMapFromCodeComment(inputSourceCode, taskFile);
184
232
  }
185
233
  }
186
234
  }
@@ -239,3 +287,7 @@ function sourceMapFromMapFile(mapFilePath) {
239
287
  const content = fs.readFileSync(mapFilePath, 'utf8');
240
288
  return JSON.parse(content);
241
289
  }
290
+ function writeToFile(filePath, fileContent) {
291
+ mkdirp.sync(path.dirname(filePath));
292
+ fs.writeFileSync(filePath, fileContent);
293
+ }
@@ -51,12 +51,20 @@ export declare class OriginSourcePattern {
51
51
  /**
52
52
  * Does the given pattern require to include the given set of files?
53
53
  *
54
+ * For example, a JavaScript bundle is compiled from several (origin) source files.
55
+ * If one of the files in the bundle is needed, then the full bundle is needed, that is,
56
+ * this function is required to return `true`.
57
+ *
54
58
  * @param originFiles - The file set to decide for include or exclude.
55
59
  *
56
- * @returns `true` if (1) all of the given files are supposed to be excluded,
57
- * or (2) if one of the files is supposed to be included.
60
+ * @returns `false` if (1) all given files are supposed to be excluded,
61
+ * or (2) `true` if at least one of the files is supposed to be included.
58
62
  */
59
63
  isAnyIncluded(originFiles: string[]): boolean;
64
+ private static normalizeGlobPattern;
65
+ private static normalizePath;
66
+ private static removeTrailingCurrentWorkingDir;
67
+ private static removePrefix;
60
68
  }
61
69
  /**
62
70
  * The actual instrumentation task.
@@ -87,6 +95,8 @@ export declare class InstrumentationTask {
87
95
  export declare class TaskResult {
88
96
  /** Number of task elements that were performed (instrumented) */
89
97
  readonly translated: number;
98
+ /** Number of task elements that were excluded because of corresponding include/exclude patterns. */
99
+ readonly excluded: number;
90
100
  /** Number of instrumentations that were taken from a cache */
91
101
  readonly translatedFromCache: number;
92
102
  /** Number of skips due to a present instrumentation */
@@ -97,7 +107,7 @@ export declare class TaskResult {
97
107
  readonly failed: number;
98
108
  /** Number of warnings that were produced during the instrumentation process */
99
109
  readonly warnings: number;
100
- constructor(translated: number, translatedFromCache: number, alreadyInstrumented: number, unsupported: number, failed: number, warnings: number);
110
+ constructor(translated: number, excluded: number, translatedFromCache: number, alreadyInstrumented: number, unsupported: number, failed: number, warnings: number);
101
111
  /**
102
112
  * Returns the sum of the present task results and the given one.
103
113
  *
@@ -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;AAI/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,2CAA2C;IAC3C,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B,gDAAgD;IAChD,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,SAAS,EAAE,MAAM;CAK7B;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC/B,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS;IAKpE;;;;;;;OAOG;IACI,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO;CAepD;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;gBAE7C,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,mBAAmB,EAAE,mBAAmB;IAM5G;;OAEG;IACH,IAAI,QAAQ,IAAI,WAAW,EAAE,CAI5B;CACD;AAED;;GAEG;AACH,qBAAa,UAAU;IACtB,iEAAiE;IACjE,SAAgB,UAAU,EAAE,MAAM,CAAC;IAEnC,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,mBAAmB,EAAE,MAAM,EAC3B,mBAAmB,EAAE,MAAM,EAC3B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;IAgBjB;;;;OAIG;IACI,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU;IAWnD;;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"}
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,2CAA2C;IAC3C,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B,gDAAgD;IAChD,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,SAAS,EAAE,MAAM;CAK7B;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC/B,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS;IAKpE;;;;;;;;;;;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;CAO3B;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;gBAE7C,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,mBAAmB,EAAE,mBAAmB;IAM5G;;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"}
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -18,11 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
18
22
  __setModuleDefault(result, mod);
19
23
  return result;
20
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
21
28
  Object.defineProperty(exports, "__esModule", { value: true });
22
29
  exports.SourceMapFileReference = exports.TaskResult = exports.InstrumentationTask = exports.OriginSourcePattern = exports.CollectorSpecifier = exports.TaskElement = exports.SourceMapReference = void 0;
23
30
  const typescript_optional_1 = require("typescript-optional");
24
31
  const commons_1 = require("@cqse/commons");
25
32
  const matching = __importStar(require("micromatch"));
33
+ const path_1 = __importDefault(require("path"));
26
34
  /**
27
35
  * An abstract source map type.
28
36
  */
@@ -70,31 +78,57 @@ exports.CollectorSpecifier = CollectorSpecifier;
70
78
  */
71
79
  class OriginSourcePattern {
72
80
  constructor(include, exclude) {
73
- this.include = include;
74
- this.exclude = exclude;
81
+ this.include = OriginSourcePattern.normalizeGlobPattern(include);
82
+ this.exclude = OriginSourcePattern.normalizeGlobPattern(exclude);
75
83
  }
76
84
  /**
77
85
  * Does the given pattern require to include the given set of files?
78
86
  *
87
+ * For example, a JavaScript bundle is compiled from several (origin) source files.
88
+ * If one of the files in the bundle is needed, then the full bundle is needed, that is,
89
+ * this function is required to return `true`.
90
+ *
79
91
  * @param originFiles - The file set to decide for include or exclude.
80
92
  *
81
- * @returns `true` if (1) all of the given files are supposed to be excluded,
82
- * or (2) if one of the files is supposed to be included.
93
+ * @returns `false` if (1) all given files are supposed to be excluded,
94
+ * or (2) `true` if at least one of the files is supposed to be included.
83
95
  */
84
96
  isAnyIncluded(originFiles) {
85
97
  var _a;
98
+ if (originFiles.length === 0) {
99
+ return true;
100
+ }
101
+ const normalizedOriginFiles = originFiles.map(OriginSourcePattern.normalizePath);
86
102
  if (this.exclude) {
87
- const matchedToExclude = matching.match(originFiles, this.exclude);
103
+ const matchedToExclude = matching.match(normalizedOriginFiles, this.exclude);
88
104
  if (originFiles.length === matchedToExclude.length) {
89
105
  return false;
90
106
  }
91
107
  }
92
108
  if (this.include) {
93
- const matchedToInclude = matching.match(originFiles, (_a = this.include) !== null && _a !== void 0 ? _a : '**');
109
+ const matchedToInclude = matching.match(normalizedOriginFiles, (_a = this.include) !== null && _a !== void 0 ? _a : '**');
94
110
  return matchedToInclude.length > 0;
95
111
  }
96
112
  return true;
97
113
  }
114
+ static normalizeGlobPattern(pattern) {
115
+ if (!pattern) {
116
+ return pattern;
117
+ }
118
+ return OriginSourcePattern.removeTrailingCurrentWorkingDir(pattern);
119
+ }
120
+ static normalizePath(toNormalize) {
121
+ return OriginSourcePattern.removeTrailingCurrentWorkingDir(toNormalize);
122
+ }
123
+ static removeTrailingCurrentWorkingDir(removeFrom) {
124
+ return OriginSourcePattern.removePrefix('webpack:///', OriginSourcePattern.removePrefix('.' + path_1.default.sep, removeFrom));
125
+ }
126
+ static removePrefix(prefix, removeFrom) {
127
+ if (removeFrom.startsWith(prefix)) {
128
+ return removeFrom.substring(prefix.length);
129
+ }
130
+ return removeFrom;
131
+ }
98
132
  }
99
133
  exports.OriginSourcePattern = OriginSourcePattern;
100
134
  /**
@@ -120,14 +154,16 @@ exports.InstrumentationTask = InstrumentationTask;
120
154
  * A summary of executing the instrumentation task.
121
155
  */
122
156
  class TaskResult {
123
- constructor(translated, translatedFromCache, alreadyInstrumented, unsupported, failed, warnings) {
157
+ constructor(translated, excluded, translatedFromCache, alreadyInstrumented, unsupported, failed, warnings) {
124
158
  commons_1.Contract.require(translated > -1);
159
+ commons_1.Contract.require(excluded > -1);
125
160
  commons_1.Contract.require(translatedFromCache > -1);
126
161
  commons_1.Contract.require(alreadyInstrumented > -1);
127
162
  commons_1.Contract.require(unsupported > -1);
128
163
  commons_1.Contract.require(failed > -1);
129
164
  commons_1.Contract.require(warnings > -1);
130
165
  this.translated = translated;
166
+ this.excluded = excluded;
131
167
  this.translatedFromCache = translatedFromCache;
132
168
  this.alreadyInstrumented = alreadyInstrumented;
133
169
  this.unsupported = unsupported;
@@ -140,13 +176,13 @@ class TaskResult {
140
176
  * @param incBy - The task result to add (as delta).
141
177
  */
142
178
  withIncrement(incBy) {
143
- return new TaskResult(this.translated + incBy.translated, this.translatedFromCache + incBy.translatedFromCache, this.alreadyInstrumented + incBy.alreadyInstrumented, this.unsupported + incBy.unsupported, this.failed + incBy.failed, this.warnings + incBy.warnings);
179
+ return new TaskResult(this.translated + incBy.translated, this.excluded + incBy.excluded, this.translatedFromCache + incBy.translatedFromCache, this.alreadyInstrumented + incBy.alreadyInstrumented, this.unsupported + incBy.unsupported, this.failed + incBy.failed, this.warnings + incBy.warnings);
144
180
  }
145
181
  /**
146
182
  * @returns the neutral task element (adding it with {@code withIncrement} does not change the result).
147
183
  */
148
184
  static neutral() {
149
- return new TaskResult(0, 0, 0, 0, 0, 0);
185
+ return new TaskResult(0, 0, 0, 0, 0, 0, 0);
150
186
  }
151
187
  /**
152
188
  * @returns a task result signaling one error.
@@ -155,7 +191,7 @@ class TaskResult {
155
191
  */
156
192
  static error(e) {
157
193
  console.error(e);
158
- return new TaskResult(0, 0, 0, 0, 1, 0);
194
+ return new TaskResult(0, 0, 0, 0, 0, 1, 0);
159
195
  }
160
196
  /**
161
197
  * @returns a task result signaling one warning.
@@ -164,7 +200,7 @@ class TaskResult {
164
200
  */
165
201
  static warning(msg) {
166
202
  console.warn(msg);
167
- return new TaskResult(0, 0, 0, 0, 0, 1);
203
+ return new TaskResult(0, 0, 0, 0, 0, 0, 1);
168
204
  }
169
205
  }
170
206
  exports.TaskResult = TaskResult;
@@ -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,CAAC;IAExB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,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,0BAA0B,CAAqB;IAEvD,0BAA0B;IAC1B,OAAO,CAAC,0BAA0B,CAAqB;;IAOvD,0EAA0E;IAC1E,sBAAsB,CAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI;IAM5D,8BAA8B;IAC9B,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAKhE,6BAA6B;IAC7B,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAKhE,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;IAkCpD;;;;;;;OAOG;IACH,OAAO,CAAC,4CAA4C;IA8BpD;;;;;;OAMG;IACH,OAAO,CAAC,0BAA0B;IASlC;;OAEG;IACI,KAAK,IAAI,mBAAmB;CAInC"}
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,CAAC;IAExB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,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,0BAA0B,CAAqB;IAEvD,0BAA0B;IAC1B,OAAO,CAAC,0BAA0B,CAAqB;;IAOvD,0EAA0E;IAC1E,sBAAsB,CAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI;IAM5D,8BAA8B;IAC9B,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAKhE,6BAA6B;IAC7B,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAKhE,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;IAkCpD;;;;;;;OAOG;IACH,OAAO,CAAC,4CAA4C;IAgCpD;;;;;;OAMG;IACH,OAAO,CAAC,0BAA0B;IASlC;;OAEG;IACI,KAAK,IAAI,mBAAmB;CAInC"}
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -128,7 +132,11 @@ class TaskBuilder {
128
132
  inputFiles.forEach(f => this.addElement(f, path.join(target, path.basename(f)), sourceMapInfo));
129
133
  }
130
134
  else {
131
- inputFiles.forEach(f => this.addElement(f, path.join(target, path.relative(input, path.basename(f))), sourceMapInfo));
135
+ inputFiles.forEach(f => {
136
+ const pathRelativeToInputDir = path.relative(input, f);
137
+ const targetFileName = path.join(target, pathRelativeToInputDir);
138
+ this.addElement(f, targetFileName, sourceMapInfo);
139
+ });
132
140
  }
133
141
  }
134
142
  else {
package/dist/src/main.js CHANGED
@@ -7,6 +7,7 @@ App_1.App.run()
7
7
  .then(result => {
8
8
  console.log('Instrumentation finished.');
9
9
  console.log(`\tInstrumented: ${result.translated}`);
10
+ console.log(`\tExcluded: ${result.excluded}`);
10
11
  console.log(`\tInstrumented from cache: ${result.translatedFromCache}`);
11
12
  console.log(`\tAlready instrumented: ${result.alreadyInstrumented}`);
12
13
  console.log(`\tUnsupported: ${result.unsupported}`);
package/dist/vaccine.js CHANGED
@@ -1,221 +1 @@
1
- (() => {
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
9
- var __commonJS = (cb, mod) => function __require() {
10
- return mod || (0, cb[Object.keys(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
- };
12
- var __reExport = (target, module, desc) => {
13
- if (module && typeof module === "object" || typeof module === "function") {
14
- for (let key of __getOwnPropNames(module))
15
- if (!__hasOwnProp.call(target, key) && key !== "default")
16
- __defProp(target, key, { get: () => module[key], enumerable: !(desc = __getOwnPropDesc(module, key)) || desc.enumerable });
17
- }
18
- return target;
19
- };
20
- var __toModule = (module) => {
21
- return __reExport(__markAsModule(__defProp(module != null ? __create(__getProtoOf(module)) : {}, "default", module && module.__esModule && "default" in module ? { get: () => module.default, enumerable: true } : { value: module, enumerable: true })), module);
22
- };
23
-
24
- // ../../node_modules/detect-node/browser.js
25
- var require_browser = __commonJS({
26
- "../../node_modules/detect-node/browser.js"(exports, module) {
27
- module.exports = false;
28
- }
29
- });
30
-
31
- // (disabled):../../node_modules/unload/dist/es/node.js
32
- var require_node = __commonJS({
33
- "(disabled):../../node_modules/unload/dist/es/node.js"() {
34
- }
35
- });
36
-
37
- // inline-worker:__inline-worker
38
- function inlineWorker(scriptText) {
39
- let blob = new Blob([scriptText], { type: "text/javascript" });
40
- let url = URL.createObjectURL(blob);
41
- let worker = new Worker(url);
42
- URL.revokeObjectURL(url);
43
- return worker;
44
- }
45
-
46
- // src/vaccine/worker/vaccine.worker.ts
47
- function Worker2() {
48
- return inlineWorker('var n=class{constructor(t){this.cachedMessages=[];this.url=t,this.socket=this.createSocket()}createSocket(){let t=new WebSocket(this.url);return t.onopen=()=>this.onopen(),t.onclose=()=>this.onclose(),t}onclose(){this.socket=this.createSocket()}onopen(){this.cachedMessages.forEach(t=>this.socket.send(t)),this.cachedMessages=[]}send(t){this.socket.readyState===WebSocket.OPEN?this.socket.send(t):this.cachedMessages.push(t)}};var o;(function(e){e.MESSAGE_TYPE_SOURCEMAP="s",e.MESSAGE_TYPE_COVERAGE="c"})(o||(o={}));var C=20,p=1e3,a=class{constructor(t,e){this.milliseconds=t;this.onCountedToZero=e;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)}},r=class{constructor(t){this.socket=t,this.cachedCoveredPositions=new Map,this.numberOfCachedPositions=0,this.flushCountdown=new a(p,()=>this.flush())}add(t){let e=t.split(":");if(e.length!==3)return;let[c,u,l]=e,i=this.cachedCoveredPositions.get(c);i||(i=new Set,this.cachedCoveredPositions.set(c,i)),i.add(`${u}:${l}`),this.numberOfCachedPositions+=1,this.flushCountdown.restartCountdown(),this.numberOfCachedPositions>=C&&this.flush()}flush(){this.numberOfCachedPositions!==0&&(this.flushCountdown.stopCountdown(),this.cachedCoveredPositions.forEach((t,e)=>{this.socket.send(`${o.MESSAGE_TYPE_COVERAGE} ${e} ${Array.from(t).join(" ")}`)}),this.cachedCoveredPositions=new Map,this.numberOfCachedPositions=0)}};console.log("Starting coverage forwarding worker.");var h=new n("ws://$REPORT_TO_HOST:$REPORT_TO_PORT/socket"),d=new r(h);onmessage=s=>{let t=s.data;t.startsWith(o.MESSAGE_TYPE_SOURCEMAP)?h.send(t):t==="unload"?d.flush():d.add(t)};\n');
49
- }
50
-
51
- // src/vaccine/utils.ts
52
- function universe() {
53
- return getWindow();
54
- }
55
- function hasWindow() {
56
- return typeof window !== "undefined";
57
- }
58
- function getWindow() {
59
- return window;
60
- }
61
- function universeAttribute(attributeName, defaultValue) {
62
- let result = universe()[attributeName];
63
- if (!result) {
64
- result = defaultValue;
65
- universe()[attributeName] = result;
66
- }
67
- return result;
68
- }
69
-
70
- // src/vaccine/Interceptor.ts
71
- var STATEMENT_COVERAGE_ID = "s";
72
- var Interceptor = class {
73
- constructor(coverageObj, path) {
74
- this.coverageObj = coverageObj;
75
- this.path = path;
76
- }
77
- get(target, prop, receiver) {
78
- const value = target[prop];
79
- if (value !== Object(value)) {
80
- return value;
81
- }
82
- return makeProxy(this.coverageObj, value, [...this.path, prop]);
83
- }
84
- set(obj, prop, value) {
85
- const fullPath = [...this.path, prop];
86
- if (fullPath[0] === STATEMENT_COVERAGE_ID) {
87
- const fileId = this.coverageObj.hash;
88
- const start = this.coverageObj.statementMap[fullPath[1]].start;
89
- universe()._$Bc(fileId, start.line, start.column);
90
- }
91
- return true;
92
- }
93
- };
94
- function makeProxy(coverage, target, path) {
95
- return new Proxy(target, new Interceptor(coverage, path));
96
- }
97
-
98
- // ../../node_modules/unload/dist/es/index.js
99
- var import_detect_node = __toModule(require_browser());
100
-
101
- // ../../node_modules/unload/dist/es/browser.js
102
- function add(fn) {
103
- if (typeof WorkerGlobalScope === "function" && self instanceof WorkerGlobalScope) {
104
- } else {
105
- if (typeof window.addEventListener !== "function")
106
- return;
107
- window.addEventListener("beforeunload", function() {
108
- fn();
109
- }, true);
110
- window.addEventListener("unload", function() {
111
- fn();
112
- }, true);
113
- }
114
- }
115
- var browser_default = {
116
- add
117
- };
118
-
119
- // ../../node_modules/unload/dist/es/index.js
120
- var import_node = __toModule(require_node());
121
- var USE_METHOD = import_detect_node.default ? import_node.default : browser_default;
122
- var LISTENERS = new Set();
123
- var startedListening = false;
124
- function startListening() {
125
- if (startedListening)
126
- return;
127
- startedListening = true;
128
- USE_METHOD.add(runAll);
129
- }
130
- function add2(fn) {
131
- startListening();
132
- if (typeof fn !== "function")
133
- throw new Error("Listener is no function");
134
- LISTENERS.add(fn);
135
- var addReturn = {
136
- remove: function remove() {
137
- return LISTENERS["delete"](fn);
138
- },
139
- run: function run() {
140
- LISTENERS["delete"](fn);
141
- return fn();
142
- }
143
- };
144
- return addReturn;
145
- }
146
- function runAll() {
147
- var promises = [];
148
- LISTENERS.forEach(function(fn) {
149
- promises.push(fn());
150
- LISTENERS["delete"](fn);
151
- });
152
- return Promise.all(promises);
153
- }
154
-
155
- // src/vaccine/protocol.ts
156
- var ProtocolMessageTypes;
157
- (function(ProtocolMessageTypes2) {
158
- ProtocolMessageTypes2["MESSAGE_TYPE_SOURCEMAP"] = "s";
159
- ProtocolMessageTypes2["MESSAGE_TYPE_COVERAGE"] = "c";
160
- })(ProtocolMessageTypes || (ProtocolMessageTypes = {}));
161
-
162
- // src/vaccine/main.ts
163
- var globalAgentObject = universeAttribute("__TS_AGENT", {});
164
- function getWorker() {
165
- return globalAgentObject._$BcWorker;
166
- }
167
- function setWorker(worker) {
168
- globalAgentObject._$BcWorker = worker;
169
- return worker;
170
- }
171
- universe().makeCoverageInterceptor = function(coverage) {
172
- const fileId = coverage.hash;
173
- if (!getWorker()) {
174
- const worker = setWorker(new Worker2());
175
- (function handleUnloading() {
176
- const protectWindowEvent = function(name) {
177
- let wrappedHandler = getWindow()[name];
178
- getWindow()[name] = function(...args) {
179
- worker.postMessage("unload");
180
- if (wrappedHandler) {
181
- return wrappedHandler.apply(this, args);
182
- }
183
- };
184
- if (hasWindow()) {
185
- Object.defineProperty(getWindow(), name, {
186
- get: function() {
187
- return wrappedHandler;
188
- },
189
- set: function(newHandler) {
190
- wrappedHandler = newHandler;
191
- }
192
- });
193
- }
194
- };
195
- protectWindowEvent("onunload");
196
- protectWindowEvent("onbeforeunload");
197
- add2(() => worker.postMessage("unload"));
198
- })();
199
- }
200
- (function sendSourceMaps() {
201
- const sentMaps = universeAttribute("sentMaps", new Set());
202
- if (coverage.inputSourceMap) {
203
- if (!sentMaps.has(coverage.path)) {
204
- getWorker().postMessage(`${ProtocolMessageTypes.MESSAGE_TYPE_SOURCEMAP} ${fileId}:${JSON.stringify(coverage.inputSourceMap)}`);
205
- sentMaps.add(coverage.path);
206
- }
207
- }
208
- })();
209
- (function registerCoverageReporter() {
210
- const reported = new Set();
211
- universe()._$Bc = (fileId2, coveredLine, coveredColumn) => {
212
- const coverageMessage = `${fileId2}:${coveredLine}:${coveredColumn}`;
213
- if (!reported.has(coverageMessage)) {
214
- getWorker().postMessage(coverageMessage);
215
- reported.add(coverageMessage);
216
- }
217
- };
218
- })();
219
- return makeProxy(coverage, coverage, []);
220
- };
221
- })();
1
+ (()=>{var R=Object.create;var h=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var j=e=>h(e,"__esModule",{value:!0});var v=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports);var L=(e,n,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of $(n))!A.call(e,o)&&o!=="default"&&h(e,o,{get:()=>n[o],enumerable:!(t=y(n,o))||t.enumerable});return e},b=e=>L(j(h(e!=null?R(I(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var E=v((K,C)=>{C.exports=!1});var W=v(()=>{});function f(e){let n=new Blob([e],{type:"text/javascript"}),t=URL.createObjectURL(n),o=new Worker(t);return URL.revokeObjectURL(t),o}function p(){return f('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(){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)}};var p=20,f=1e3,c=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)}},n=class{constructor(e){this.socket=e,this.cachedCoveredPositions=new Map,this.numberOfCachedPositions=0,this.flushCountdown=new c(f,()=>this.flush())}add(e){let t=e.split(":");if(t.length!==3)return;let[r,d,u]=t,o=this.cachedCoveredPositions.get(r);o||(o=new Set,this.cachedCoveredPositions.set(r,o)),o.add(`${d}:${u}`),this.numberOfCachedPositions+=1,this.flushCountdown.restartCountdown(),this.numberOfCachedPositions>=p&&this.flush()}flush(){this.numberOfCachedPositions!==0&&(this.flushCountdown.stopCountdown(),this.cachedCoveredPositions.forEach((e,t)=>{this.socket.send(`${"c"} ${t} ${Array.from(e).join(" ")}`)}),this.cachedCoveredPositions=new Map,this.numberOfCachedPositions=0)}};console.log("Starting coverage forwarding worker.");var h=new i("ws://$REPORT_TO_HOST:$REPORT_TO_PORT/socket"),a=new n(h);onmessage=s=>{let e=s.data;e.startsWith("s")?h.send(e):e==="unload"?a.flush():a.add(e)};\n')}function u(){return c()}function k(){return typeof window!="undefined"}function c(){return window}function m(e,n){let t=u()[e];return t||(t=n,u()[e]=t),t}var D="s",S=class{constructor(n,t){this.coverageObj=n;this.path=t}get(n,t,o){let r=n[t];return r!==Object(r)?r:w(this.coverageObj,r,[...this.path,t])}set(n,t,o){let r=[...this.path,t];if(r[0]===D){let i=this.coverageObj.hash,s=this.coverageObj.statementMap[r[1]].start;u()._$Bc(i,s.line,s.column)}return!0}};function w(e,n,t){return new Proxy(n,new S(e,t))}var M=b(E());function H(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 O={add:H};var T=b(W()),B=M.default?T.default:O,d=new Set,x=!1;function G(){x||(x=!0,B.add(U))}function P(e){if(G(),typeof e!="function")throw new Error("Listener is no function");d.add(e);var n={remove:function(){return d.delete(e)},run:function(){return d.delete(e),e()}};return n}function U(){var e=[];return d.forEach(function(n){e.push(n()),d.delete(n)}),Promise.all(e)}var l;(function(t){t.MESSAGE_TYPE_SOURCEMAP="s",t.MESSAGE_TYPE_COVERAGE="c"})(l||(l={}));var _=m("__TS_AGENT",{});function g(){return _._$BcWorker}function N(e){return _._$BcWorker=e,e}u().makeCoverageInterceptor=function(e){let n=e.hash;if(!g()){let t=N(new p);(function(){let r=function(i){let s=c()[i];c()[i]=function(...a){if(t.postMessage("unload"),s)return s.apply(this,a)},k()&&Object.defineProperty(c(),i,{get:function(){return s},set:function(a){s=a}})};r("onunload"),r("onbeforeunload"),P(()=>t.postMessage("unload"))})()}return function(){let o=m("sentMaps",new Set);e.inputSourceMap&&(o.has(e.path)||(g().postMessage(`${l.MESSAGE_TYPE_SOURCEMAP} ${n}:${JSON.stringify(e.inputSourceMap)}`),o.add(e.path)))}(),function(){let o=new Set;u()._$Bc=(r,i,s)=>{let a=`${r}:${i}:${s}`;o.has(a)||(g().postMessage(a),o.add(a))}}(),w(e,e,[])};})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamscale/javascript-instrumenter",
3
- "version": "0.0.1-beta.3",
3
+ "version": "0.0.1-beta.33",
4
4
  "description": "Istanbul-based coverage instrumenter with coverage forwarding via WebSockets",
5
5
  "main": "dist/src/main.js",
6
6
  "bin": "dist/src/main.js",
@@ -12,24 +12,27 @@
12
12
  "url": "https://github.com/cqse/teamscale-javascript-profiler.git"
13
13
  },
14
14
  "scripts": {
15
+ "prepublishOnly": "yarn clean && yarn build",
15
16
  "clean": "rimraf dist tsconfig.tsbuildinfo",
16
- "build": "tsc --project tsconfig.json && node esbuild.mjs",
17
- "instrument": "node dist/src/main.js",
17
+ "build": "tsc --project tsconfig.json && yarn buildVaccine",
18
+ "buildVaccine": "node esbuild.mjs",
19
+ "instrumenter": "node dist/src/main.js",
18
20
  "test": "yarn build && NODE_OPTIONS='--experimental-vm-modules' jest --forceExit --coverage --silent=true --detectOpenHandles"
19
21
  },
20
22
  "files": [
21
23
  "dist/**/*"
22
24
  ],
23
25
  "devDependencies": {
24
- "@babel/core": "^7.14.0",
26
+ "@babel/core": "^7.17.5",
25
27
  "@babel/plugin-transform-modules-commonjs": "^7.15.4",
26
28
  "@babel/preset-env": "^7.14.1",
27
29
  "@types/async": "^3.2.6",
30
+ "@types/bunyan": "^1.8.8",
28
31
  "@types/convert-source-map": "^1.5.1",
29
32
  "@types/glob": "^7.1.3",
30
33
  "@types/istanbul-lib-instrument": "^1.7.4",
31
34
  "@types/jest": "^27.0.1",
32
- "@types/mkdirp": "^1.0.1",
35
+ "@types/mkdirp": "^1.0.2",
33
36
  "@types/node": "^15.0.1",
34
37
  "@types/source-map": "^0.5.7",
35
38
  "@types/ws": "^7.4.4",
@@ -44,10 +47,15 @@
44
47
  "typescript": "^4.4.3"
45
48
  },
46
49
  "dependencies": {
50
+ "@babel/generator": "^7.17.3",
51
+ "@babel/parser": "^7.17.3",
52
+ "@babel/traverse": "^7.17.3",
53
+ "@babel/types": "^7.17.0",
47
54
  "@cqse/commons": "^0.0.1-beta.1",
48
55
  "@types/micromatch": "^4.0.2",
49
56
  "argparse": "^2.0.1",
50
- "async": "^3.2.0",
57
+ "async": "^3.2.3",
58
+ "bunyan": "^1.8.15",
51
59
  "convert-source-map": "^1.7.0",
52
60
  "foreground-child": "^2.0.0",
53
61
  "glob": "^7.1.7",
@@ -55,11 +63,10 @@
55
63
  "micromatch": "4.0.4",
56
64
  "mkdirp": "^1.0.4",
57
65
  "nyc": "^15.1.0",
58
- "source-map": "0.6.1",
66
+ "source-map": "0.7.3",
59
67
  "typescript-optional": "^2.0.1",
60
68
  "unload": "^2.2.0",
61
- "web-worker": "^1.0.0",
62
- "winston": "^3.3.3"
69
+ "web-worker": "^1.0.0"
63
70
  },
64
71
  "publishConfig": {
65
72
  "access": "public"