@teamscale/javascript-instrumenter 0.0.1-beta.54 → 0.0.1-beta.59

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/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+
package/README.md CHANGED
@@ -17,12 +17,12 @@ information is produced and (2) forwarded to the Collector.
17
17
  ## Building
18
18
 
19
19
  The Instrumenter is written in TypeScript/JavaScript. For building and running it,
20
- NodeJs (>= v14) and Yarn are needed as prerequisites.
20
+ NodeJs (>= v16) and pnpm are needed as prerequisites.
21
21
 
22
22
  ```
23
- yarn clean
24
- yarn install
25
- yarn build
23
+ pnpm clean
24
+ pnpm install
25
+ pnpm build
26
26
  ```
27
27
 
28
28
  ## Workflow Integration
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamscale/javascript-instrumenter",
3
- "version": "0.0.1-beta.54",
3
+ "version": "0.0.1-beta.59",
4
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",
@@ -12,60 +12,61 @@
12
12
  "url": "https://github.com/cqse/teamscale-javascript-profiler.git"
13
13
  },
14
14
  "scripts": {
15
- "prepublishOnly": "yarn clean && yarn build",
15
+ "prepublishOnly": "pnpm clean && pnpm build",
16
16
  "clean": "rimraf dist tsconfig.tsbuildinfo",
17
- "build": "tsc --project tsconfig.json && yarn buildVaccine",
17
+ "build": "tsc --project tsconfig.json && pnpm buildVaccine",
18
18
  "buildVaccine": "node esbuild.mjs",
19
19
  "instrumenter": "node dist/src/main.js",
20
- "test": "yarn build && NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=8192' jest --forceExit --coverage --silent=true --detectOpenHandles"
20
+ "test": "pnpm 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.22.5",
26
+ "@babel/core": "^7.22.8",
27
27
  "@babel/plugin-transform-modules-commonjs": "^7.22.5",
28
- "@babel/preset-env": "^7.22.5",
29
- "@types/async": "^3.2.6",
28
+ "@babel/preset-env": "^7.22.7",
29
+ "@types/argparse": "^2.0.10",
30
+ "@types/async": "^3.2.20",
31
+ "@types/babel__generator": "^7.6.4",
32
+ "@types/babel__traverse": "^7.20.1",
30
33
  "@types/bunyan": "^1.8.8",
31
- "@types/convert-source-map": "^1.5.1",
32
- "@types/glob": "^7.1.3",
34
+ "@types/convert-source-map": "^2.0.0",
35
+ "@types/glob": "^8.1.0",
33
36
  "@types/istanbul-lib-instrument": "^1.7.4",
34
- "@types/jest": "^29.5.1",
35
- "@types/mkdirp": "^1.0.2",
36
- "@types/node": "^15.0.1",
37
- "@types/source-map": "^0.5.7",
38
- "@types/ws": "^7.4.4",
39
- "babel-jest": "^29.5.0",
40
- "esbuild": "^0.13.3",
37
+ "@types/jest": "^29.5.3",
38
+ "@types/node": "^20.4.1",
39
+ "@types/ws": "^8.5.5",
40
+ "babel-jest": "^29.6.1",
41
+ "esbuild": "^0.18.11",
41
42
  "esbuild-plugin-inline-worker": "^0.1.1",
42
- "jest": "^29.5.0",
43
- "rimraf": "^3.0.2",
44
- "ts-jest": "^29.1.0",
45
- "ts-node": "^10.2.1",
46
- "tslib": "^2.2.0",
47
- "typescript": "^4.4.3"
43
+ "jest": "^29.6.1",
44
+ "rimraf": "^5.0.1",
45
+ "ts-jest": "^29.1.1",
46
+ "ts-node": "^10.9.1",
47
+ "tslib": "^2.6.0",
48
+ "typescript": "^5.1.6"
48
49
  },
49
50
  "dependencies": {
50
- "@babel/generator": "^7.22.5",
51
- "@babel/parser": "^7.22.5",
52
- "@babel/traverse": "^7.22.5",
51
+ "@babel/generator": "^7.22.7",
52
+ "@babel/parser": "^7.22.7",
53
+ "@babel/traverse": "^7.22.8",
53
54
  "@babel/types": "^7.22.5",
54
- "@cqse/commons": "0.0.1-beta.52",
55
+ "@cqse/commons": "workspace:../cqse-commons",
55
56
  "@types/micromatch": "^4.0.2",
56
57
  "argparse": "^2.0.1",
57
58
  "async": "^3.2.4",
58
59
  "bunyan": "^1.8.15",
59
- "convert-source-map": "^1.7.0",
60
- "foreground-child": "^2.0.0",
61
- "glob": "^7.1.7",
60
+ "convert-source-map": "^2.0.0",
61
+ "foreground-child": "^3.1.1",
62
+ "glob": "^10.3.3",
62
63
  "istanbul-lib-instrument": "^5.2.1",
63
- "micromatch": "4.0.4",
64
- "mkdirp": "^1.0.4",
64
+ "micromatch": "4.0.5",
65
+ "mkdirp": "^3.0.1",
65
66
  "source-map": "0.7.4",
66
67
  "typescript-optional": "^2.0.1",
67
- "unload": "^2.2.0",
68
- "web-worker": "^1.0.0"
68
+ "unload": "^2.4.1",
69
+ "web-worker": "^1.2.0"
69
70
  },
70
71
  "publishConfig": {
71
72
  "access": "public"
@@ -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;IAe9C;;;OAGG;WACW,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAyCtE;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAyC1B;;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;IAS1G,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,EAA0C,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAGxF,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;IAe9C;;;OAGG;WACW,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAwCtE;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAyC1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IA+B1B;;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;IAS1G,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAIxC,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAajC"}
package/dist/src/App.js CHANGED
@@ -34,7 +34,7 @@ const TaskBuilder_1 = require("./instrumenter/TaskBuilder");
34
34
  const path = __importStar(require("path"));
35
35
  const package_json_1 = require("../package.json");
36
36
  const fs_1 = require("fs");
37
- const mkdirp_1 = __importDefault(require("mkdirp"));
37
+ const mkdirp_1 = require("mkdirp");
38
38
  const bunyan_1 = __importDefault(require("bunyan"));
39
39
  /**
40
40
  * Entry points of the instrumenter, including command line argument parsing.
@@ -51,8 +51,8 @@ class App {
51
51
  const config = parser.parse_args();
52
52
  // Build the logger
53
53
  const logger = this.buildLogger(config);
54
- logger.debug("Retrieved arguments", process.argv);
55
- logger.debug("Translated arguments to config", config);
54
+ logger.debug('Retrieved arguments', process.argv);
55
+ logger.debug('Translated arguments to config', config);
56
56
  // Run the instrumenter with the given configuration.
57
57
  return this.runForConfigArguments(config, logger);
58
58
  }
@@ -117,7 +117,7 @@ class App {
117
117
  help: 'Path (directory or file name) to write the instrumented version to.'
118
118
  });
119
119
  parser.add_argument('-s', '--source-map', {
120
- help: 'External location of source-map files to consider.',
120
+ help: 'External location of source-map files to consider.'
121
121
  });
122
122
  parser.add_argument('-c', '--collector', {
123
123
  help: 'The collector (`host:port` or `wss://host:port/` or `ws://host:port/`) to send coverage information to.',
@@ -146,7 +146,7 @@ class App {
146
146
  */
147
147
  static buildLogger(config) {
148
148
  const logfilePath = 'logs/instrumenter.log';
149
- mkdirp_1.default.sync(path.dirname(logfilePath));
149
+ mkdirp_1.mkdirp.sync(path.dirname(logfilePath));
150
150
  const logLevel = config.debug ? 'debug' : 'error';
151
151
  return bunyan_1.default.createLogger({
152
152
  name: 'Instrumenter',
@@ -154,6 +154,7 @@ class App {
154
154
  {
155
155
  level: logLevel,
156
156
  stream: {
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
157
158
  write: (rec) => {
158
159
  console.log('[%s] %s: %s', rec.time.toISOString(), bunyan_1.default.nameFromLevel[rec.level], rec.msg);
159
160
  }
@@ -183,12 +184,12 @@ class App {
183
184
  this.postprocessConfig(config);
184
185
  const task = this.createInstrumentationTask(config);
185
186
  commons_1.Contract.require(task.elements.length > 0, 'The instrumentation task must not be empty.');
186
- return this.createInstrumenter(logger !== null && logger !== void 0 ? logger : this.buildDummyLogger()).instrument(task);
187
+ return this.createInstrumenter(logger !== null && logger !== void 0 ? logger : this.buildDummyLogger(), task.collector).instrument(task);
187
188
  }
188
189
  static createInstrumentationTask(config) {
189
190
  return new TaskBuilder_1.TaskBuilder().addFromConfig(config).build();
190
191
  }
191
- static createInstrumenter(logger) {
192
+ static createInstrumenter(logger, collector) {
192
193
  // We have to deal with two different `__dirname` versions,
193
194
  // which depends on whether we run from within the IDE or from
194
195
  // the command line:
@@ -196,10 +197,10 @@ class App {
196
197
  const pathVariant1 = path.join(__dirname, '../vaccine.js');
197
198
  const pathVariant2 = path.join(__dirname, '../dist/vaccine.js');
198
199
  if ((0, fs_1.existsSync)(pathVariant1)) {
199
- return new Instrumenter_1.IstanbulInstrumenter(pathVariant1, logger);
200
+ return new Instrumenter_1.IstanbulInstrumenter(pathVariant1, logger, collector);
200
201
  }
201
202
  else {
202
- return new Instrumenter_1.IstanbulInstrumenter(pathVariant2, logger);
203
+ return new Instrumenter_1.IstanbulInstrumenter(pathVariant2, logger, collector);
203
204
  }
204
205
  }
205
206
  }
@@ -1,3 +1,4 @@
1
+ import { RawSourceMap } from 'source-map';
1
2
  /**
2
3
  * Does the given `path` point to an existing file?
3
4
  */
@@ -10,6 +11,10 @@ export declare function isExistingDirectory(path: string): boolean;
10
11
  * Ensure that the given directory `path` exists.
11
12
  */
12
13
  export declare function ensureExistingDirectory(path: string): void;
14
+ /**
15
+ * Given a root folder find a folder with a given name.
16
+ */
17
+ export declare function findSubFolders(startFromFolder: string, folderName: string): string[];
13
18
  /**
14
19
  * Is the given directory empty?
15
20
  */
@@ -20,4 +25,10 @@ export declare function isDirectoryEmpty(path: string): boolean;
20
25
  * @param toExpand - The Glob pattern.
21
26
  */
22
27
  export declare function expandToFileSet(toExpand: string): string[];
28
+ /**
29
+ * Read a source map from a source map file.
30
+ *
31
+ * @param mapFilePath
32
+ */
33
+ export declare function sourceMapFromMapFile(mapFilePath: string): RawSourceMap | undefined;
23
34
  //# sourceMappingURL=FileSystem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/FileSystem.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAU1D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAc1D"}
1
+ {"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/FileSystem.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAU1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBpF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAc1D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAGlF"}
@@ -26,10 +26,10 @@ 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.expandToFileSet = exports.isDirectoryEmpty = exports.ensureExistingDirectory = exports.isExistingDirectory = exports.isExistingFile = void 0;
29
+ exports.sourceMapFromMapFile = exports.expandToFileSet = exports.isDirectoryEmpty = exports.findSubFolders = exports.ensureExistingDirectory = exports.isExistingDirectory = exports.isExistingFile = void 0;
30
30
  const commons_1 = require("@cqse/commons");
31
31
  const fs = __importStar(require("fs"));
32
- const mkdirp_1 = __importDefault(require("mkdirp"));
32
+ const mkdirp = __importStar(require("mkdirp"));
33
33
  const path_1 = __importDefault(require("path"));
34
34
  const glob_1 = require("glob");
35
35
  /**
@@ -51,13 +51,32 @@ exports.isExistingDirectory = isExistingDirectory;
51
51
  */
52
52
  function ensureExistingDirectory(path) {
53
53
  if (!fs.existsSync(path)) {
54
- mkdirp_1.default.sync(path);
54
+ mkdirp.sync(path);
55
55
  }
56
56
  if (!fs.lstatSync(path).isDirectory()) {
57
57
  throw new commons_1.InvalidConfigurationException(`The specified path '${path}' does not point to an existing directory!`);
58
58
  }
59
59
  }
60
60
  exports.ensureExistingDirectory = ensureExistingDirectory;
61
+ /**
62
+ * Given a root folder find a folder with a given name.
63
+ */
64
+ function findSubFolders(startFromFolder, folderName) {
65
+ const matches = [];
66
+ const entries = fs.readdirSync(startFromFolder, { withFileTypes: true });
67
+ for (const entry of entries) {
68
+ const fullPath = path_1.default.join(startFromFolder, entry.name);
69
+ if (entry.name === folderName && entry.isDirectory()) {
70
+ matches.push(fullPath);
71
+ }
72
+ if (entry.isDirectory()) {
73
+ const nestedMatches = findSubFolders(fullPath, folderName);
74
+ matches.push(...nestedMatches);
75
+ }
76
+ }
77
+ return matches;
78
+ }
79
+ exports.findSubFolders = findSubFolders;
61
80
  /**
62
81
  * Is the given directory empty?
63
82
  */
@@ -84,3 +103,13 @@ function expandToFileSet(toExpand) {
84
103
  return glob_1.glob.sync(globPattern, { nodir: true });
85
104
  }
86
105
  exports.expandToFileSet = expandToFileSet;
106
+ /**
107
+ * Read a source map from a source map file.
108
+ *
109
+ * @param mapFilePath
110
+ */
111
+ function sourceMapFromMapFile(mapFilePath) {
112
+ const content = fs.readFileSync(mapFilePath, 'utf8');
113
+ return JSON.parse(content);
114
+ }
115
+ exports.sourceMapFromMapFile = sourceMapFromMapFile;
@@ -19,16 +19,16 @@ export interface IInstrumenter {
19
19
  */
20
20
  export declare class IstanbulInstrumenter implements IInstrumenter {
21
21
  /**
22
- * The path to the vaccine to inject. The vaccine is a JavaScript
22
+ * The vaccine to inject. The vaccine is a JavaScript
23
23
  * file with the code to forward the coverage information
24
24
  * produced by the Istanbul instrumentation.
25
25
  */
26
- private readonly vaccineFilePath;
26
+ private readonly vaccineSource;
27
27
  /**
28
28
  * The logger instance to log to.
29
29
  */
30
30
  private logger;
31
- constructor(vaccineFilePath: string, logger: Logger);
31
+ constructor(vaccineFilePath: string, logger: Logger, collector: CollectorSpecifier);
32
32
  /**
33
33
  * {@inheritDoc #IInstrumenter.instrument}
34
34
  */
@@ -42,7 +42,9 @@ export declare class IstanbulInstrumenter implements IInstrumenter {
42
42
  * @param sourcePattern - A pattern to restrict the instrumentation to only a fraction of the task element.
43
43
  * @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
44
44
  */
45
- instrumentOne(collector: CollectorSpecifier, taskElement: TaskElement, excludeBundles: FileExcludePattern, sourcePattern: OriginSourcePattern, dumpOriginsFile: string | undefined): Promise<TaskResult>;
45
+ instrumentOne(taskElement: TaskElement, excludeBundles: FileExcludePattern, sourcePattern: OriginSourcePattern, dumpOriginsFile: string | undefined): Promise<TaskResult>;
46
+ private instrumentBundle;
47
+ private writeBundleFile;
46
48
  private removeUnwantedInstrumentation;
47
49
  /**
48
50
  * Loads the vaccine from the vaccine file and adjusts some template parameters.
@@ -86,10 +88,4 @@ export declare function loadInputSourceMap(inputSource: string, taskFile: string
86
88
  * @param sourceFilePath - The file name the code was loaded from.
87
89
  */
88
90
  export declare function sourceMapFromCodeComment(sourcecode: string, sourceFilePath: string): RawSourceMap | undefined;
89
- /**
90
- * Read a source map from a source map file.
91
- *
92
- * @param mapFilePath
93
- */
94
- export declare function sourceMapFromMapFile(mapFilePath: string): RawSourceMap | undefined;
95
91
  //# 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,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAO7D,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;IAsBhE;;;;;;;;OAQG;IACG,aAAa,CAClB,SAAS,EAAE,kBAAkB,EAC7B,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,kBAAkB,EAClC,aAAa,EAAE,mBAAmB,EAClC,eAAe,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAAC,UAAU,CAAC;YAmHR,6BAA6B;IA8C3C;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAcpC,0GAA0G;IAC1G,OAAO,CAAC,WAAW;IASnB,uHAAuH;IACvH,OAAO,CAAC,4BAA4B;CASpC;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAClC,kBAAkB,EAAE,MAAM,EAC1B,0BAA0B,EAAE,MAAM,GAChC,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAUxC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,qBAAqB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GACjD,YAAY,GAAG,SAAS,CAU1B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAsC7G;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAGlF"}
1
+ {"version":3,"file":"Instrumenter.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Instrumenter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,kBAAkB,EAClB,kBAAkB,EAElB,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAElB,WAAW,EACX,UAAU,EACV,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAO7D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,MAAM,MAAM,QAAQ,CAAC;AAK5B,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;AA0BD;;GAEG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAS;gBAEX,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB;IAUlF;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBhE;;;;;;;;OAQG;IACG,aAAa,CAClB,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,kBAAkB,EAClC,aAAa,EAAE,mBAAmB,EAClC,eAAe,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAAC,UAAU,CAAC;YAsER,gBAAgB;IAiE9B,OAAO,CAAC,eAAe;YAkCT,6BAA6B;IA8C3C;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;;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;AAsBD;;;;;;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,CAwC7G"}
@@ -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.sourceMapFromMapFile = exports.sourceMapFromCodeComment = exports.loadInputSourceMap = exports.loadSourceMap = exports.IstanbulInstrumenter = exports.IS_INSTRUMENTED_TOKEN = void 0;
29
+ 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");
@@ -38,14 +38,38 @@ const convertSourceMap = __importStar(require("convert-source-map"));
38
38
  const Postprocessor_1 = require("./Postprocessor");
39
39
  const typescript_optional_1 = require("typescript-optional");
40
40
  const async_1 = __importDefault(require("async"));
41
+ const WebToolkit_1 = require("./WebToolkit");
42
+ const FileSystem_1 = require("./FileSystem");
41
43
  exports.IS_INSTRUMENTED_TOKEN = '/** $IS_JS_PROFILER_INSTRUMENTED=true **/';
44
+ function readBundle(bundleContent, taskElement) {
45
+ const baseNameMatch = path.basename(taskElement.fromFile).match('^([a-zA-Z0-9]*).cache.js');
46
+ const directories = path.dirname(taskElement.fromFile).split(path.sep);
47
+ if (baseNameMatch && directories.length > 1) {
48
+ const fragmentId = (0, WebToolkit_1.determineGwtFileUid)(taskElement.fromFile);
49
+ const functionCall = bundleContent.match('^([^(]+)\\((.*)\\);*\\s*$');
50
+ if (fragmentId && functionCall && functionCall.length === 3) {
51
+ const callInfos = (0, WebToolkit_1.extractGwtCallInfos)(bundleContent);
52
+ if (callInfos) {
53
+ return {
54
+ type: 'gwt',
55
+ content: bundleContent,
56
+ fragmentId,
57
+ functionName: callInfos.functionName,
58
+ codeAsArrayArgument: callInfos.codeAsArrayArgument,
59
+ codeArguments: callInfos.codeArguments
60
+ };
61
+ }
62
+ }
63
+ }
64
+ return { type: 'javascript', content: bundleContent, codeArguments: [bundleContent] };
65
+ }
42
66
  /**
43
67
  * An instrumenter based on the IstanbulJs instrumentation and coverage framework.
44
68
  */
45
69
  class IstanbulInstrumenter {
46
- constructor(vaccineFilePath, logger) {
47
- this.vaccineFilePath = commons_1.Contract.requireNonEmpty(vaccineFilePath);
70
+ constructor(vaccineFilePath, logger, collector) {
48
71
  commons_1.Contract.require(fs.existsSync(vaccineFilePath), `The vaccine file to inject "${vaccineFilePath}" must exist!\nCWD:${process.cwd()}`);
72
+ this.vaccineSource = this.loadVaccine(commons_1.Contract.requireNonEmpty(vaccineFilePath), collector);
49
73
  this.logger = logger;
50
74
  }
51
75
  /**
@@ -57,7 +81,7 @@ class IstanbulInstrumenter {
57
81
  // not overuse memory (NodeJS has only limited mem to use).
58
82
  return async_1.default
59
83
  .mapLimit(task.elements, 1, async (taskElement) => {
60
- return await this.instrumentOne(task.collector, taskElement, task.excludeFilesPattern, task.originSourcePattern, task.dumpOriginsFile);
84
+ return await this.instrumentOne(taskElement, task.excludeFilesPattern, task.originSourcePattern, task.dumpOriginsFile);
61
85
  })
62
86
  .then(results => {
63
87
  return results.reduce((prev, curr) => {
@@ -74,8 +98,7 @@ class IstanbulInstrumenter {
74
98
  * @param sourcePattern - A pattern to restrict the instrumentation to only a fraction of the task element.
75
99
  * @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
76
100
  */
77
- async instrumentOne(collector, taskElement, excludeBundles, sourcePattern, dumpOriginsFile) {
78
- var _a, _b;
101
+ async instrumentOne(taskElement, excludeBundles, sourcePattern, dumpOriginsFile) {
79
102
  // Not all file types are supported by the instrumenter
80
103
  if (!this.isFileTypeSupported(taskElement.fromFile)) {
81
104
  if (!taskElement.isInPlace()) {
@@ -83,6 +106,16 @@ class IstanbulInstrumenter {
83
106
  }
84
107
  return new Task_1.TaskResult(0, 0, 0, 0, 1, 0, 0);
85
108
  }
109
+ // Read the (supported) file
110
+ const bundleContent = fs.readFileSync(taskElement.fromFile, 'utf8');
111
+ const inputBundle = readBundle(bundleContent, taskElement);
112
+ // We skip files that we have already instrumented
113
+ if (inputBundle.content.startsWith(exports.IS_INSTRUMENTED_TOKEN)) {
114
+ if (!taskElement.isInPlace()) {
115
+ writeToFile(taskElement.toFile, inputBundle.content);
116
+ }
117
+ return new Task_1.TaskResult(0, 0, 0, 1, 0, 0, 0);
118
+ }
86
119
  // We might want to skip the instrumentation of the file
87
120
  if (excludeBundles.isExcluded(taskElement.fromFile)) {
88
121
  if (!taskElement.isInPlace()) {
@@ -90,68 +123,84 @@ class IstanbulInstrumenter {
90
123
  }
91
124
  return new Task_1.TaskResult(0, 1, 0, 0, 0, 0, 0);
92
125
  }
93
- const inputFileSource = fs.readFileSync(taskElement.fromFile, 'utf8');
94
- // We skip files that we have already instrumented
95
- if (inputFileSource.startsWith(exports.IS_INSTRUMENTED_TOKEN)) {
96
- if (!taskElement.isInPlace()) {
97
- writeToFile(taskElement.toFile, inputFileSource);
98
- }
99
- return new Task_1.TaskResult(0, 0, 0, 1, 0, 0, 0);
100
- }
101
126
  // Report progress
102
127
  this.logger.info(`Instrumenting "${path.basename(taskElement.fromFile)}"`);
103
- let finalSourceMap;
104
- let instrumentedSource;
128
+ // Load the bundles source maps
129
+ const inputSourceMaps = loadInputSourceMaps(taskElement.fromFile, inputBundle, taskElement.externalSourceMapFile);
130
+ if (inputSourceMaps.length === 0) {
131
+ return Task_1.TaskResult.warning(`Failed loading input source map for ${taskElement.fromFile}.`);
132
+ }
105
133
  // We try to perform the instrumentation with different
106
134
  // alternative configurations of the instrumenter.
107
135
  const configurationAlternatives = this.configurationAlternativesFor(taskElement);
108
136
  for (let i = 0; i < configurationAlternatives.length; i++) {
109
137
  const configurationAlternative = configurationAlternatives[i];
110
- let inputSourceMap;
111
138
  try {
112
- const instrumenter = istanbul.createInstrumenter(configurationAlternative);
113
- inputSourceMap = loadInputSourceMap(inputFileSource, taskElement.fromFile, taskElement.externalSourceMapFile);
114
- // Based on the source maps of the file to instrument, we can now
115
- // decide if we should NOT write an instrumented version of it
116
- // and use the original code instead and write it to the target path.
117
- //
118
- const originSourceFiles = (_a = inputSourceMap === null || inputSourceMap === void 0 ? void 0 : inputSourceMap.sources) !== null && _a !== void 0 ? _a : [];
119
- if (dumpOriginsFile) {
120
- this.dumpOrigins(dumpOriginsFile, originSourceFiles);
121
- }
122
- // The main instrumentation (adding coverage statements) is performed now:
123
- instrumentedSource = instrumenter.instrumentSync(inputFileSource, taskElement.fromFile, inputSourceMap);
124
- this.logger.debug('Instrumentation source maps to:', (_b = instrumenter.lastSourceMap()) === null || _b === void 0 ? void 0 : _b.sources);
125
- // In case of a bundle, the initial instrumentation step might have added
126
- // too much and undesired instrumentations. Remove them now.
127
- const instrumentedSourcemap = instrumenter.lastSourceMap();
128
- let instrumentedAndCleanedSource = await this.removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern, instrumentedSourcemap);
129
- instrumentedAndCleanedSource = instrumentedAndCleanedSource
130
- .replace(/actualCoverage\s*=\s*coverage\[path\]/g, 'actualCoverage=_$registerCoverageObject(coverage[path])')
131
- .replace(/new Function\("return this"\)\(\)/g, "typeof window === 'object' ? window : this");
132
- // The process also can result in a new source map that we will append in the result.
133
- //
134
- // `lastSourceMap` === Sourcemap for the last file that was instrumented.
135
- finalSourceMap = convertSourceMap.fromObject(instrumenter.lastSourceMap()).toComment();
136
- // We now can glue together the final version of the instrumented file.
137
- const vaccineSource = this.loadVaccine(collector);
138
- writeToFile(taskElement.toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${vaccineSource} ${instrumentedAndCleanedSource} \n${finalSourceMap}`);
139
- return new Task_1.TaskResult(1, 0, 0, 0, 0, 0, 0);
139
+ return await this.instrumentBundle(taskElement, inputBundle, inputSourceMaps, configurationAlternative, dumpOriginsFile, sourcePattern);
140
140
  }
141
141
  catch (e) {
142
142
  // If also the last configuration alternative failed,
143
143
  // we emit a corresponding warning or signal an error.
144
144
  if (i === configurationAlternatives.length - 1) {
145
- if (!inputSourceMap) {
146
- return Task_1.TaskResult.warning(`Failed loading input source map for ${taskElement.fromFile}: ${e.message}`);
147
- }
148
- writeToFile(taskElement.toFile, inputFileSource);
145
+ writeToFile(taskElement.toFile, inputBundle.content);
149
146
  return Task_1.TaskResult.error(e);
150
147
  }
151
148
  }
152
149
  }
153
150
  return new Task_1.TaskResult(0, 0, 0, 0, 0, 1, 0);
154
151
  }
152
+ async instrumentBundle(taskElement, inputBundle, inputSourceMaps, configurationAlternative, dumpOriginsFile, sourcePattern) {
153
+ var _a, _b;
154
+ const instrumenter = istanbul.createInstrumenter(configurationAlternative);
155
+ // Based on the source maps of the file to instrument, we can now
156
+ // decide if we should NOT write an instrumented version of it
157
+ // and use the original code instead and write it to the target path.
158
+ //
159
+ for (const inputSourceMap of inputSourceMaps.filter(map => map)) {
160
+ const originSourceFiles = (_a = inputSourceMap === null || inputSourceMap === void 0 ? void 0 : inputSourceMap.sources) !== null && _a !== void 0 ? _a : [];
161
+ if (dumpOriginsFile) {
162
+ this.dumpOrigins(dumpOriginsFile, originSourceFiles);
163
+ }
164
+ }
165
+ // The main instrumentation (adding coverage statements) is performed now:
166
+ const instrumentedSources = [];
167
+ const finaleSourceMaps = [];
168
+ for (let i = 0; i < inputBundle.codeArguments.length; i++) {
169
+ const instrumented = instrumenter.instrumentSync(inputBundle.codeArguments[i], taskElement.fromFile, inputSourceMaps[i]);
170
+ // In case of a bundle, the initial instrumentation step might have added
171
+ // too much and undesired instrumentations. Remove them now.
172
+ const instrumentedSourcemap = instrumenter.lastSourceMap();
173
+ let instrumentedAndCleanedSource = await this.removeUnwantedInstrumentation(taskElement, instrumented, configurationAlternative, sourcePattern, instrumentedSourcemap);
174
+ instrumentedAndCleanedSource = instrumentedAndCleanedSource
175
+ .replace(/actualCoverage\s*=\s*coverage\[path\]/g, 'actualCoverage=_$registerCoverageObject(coverage[path])')
176
+ .replace(/new Function\("return this"\)\(\)/g, "typeof window === 'object' ? window : this");
177
+ instrumentedSources.push(instrumentedAndCleanedSource);
178
+ // The process also can result in a new source map that we will append in the result.
179
+ //
180
+ // `lastSourceMap` === Sourcemap for the last file that was instrumented.
181
+ finaleSourceMaps.push(convertSourceMap.fromObject(instrumenter.lastSourceMap()).toComment());
182
+ this.logger.debug('Instrumentation source maps to:', (_b = instrumenter.lastSourceMap()) === null || _b === void 0 ? void 0 : _b.sources);
183
+ }
184
+ this.writeBundleFile(taskElement.toFile, inputBundle, instrumentedSources, finaleSourceMaps);
185
+ return new Task_1.TaskResult(1, 0, 0, 0, 0, 0, 0);
186
+ }
187
+ writeBundleFile(toFile, inputBundle, instrumentedSources, finalSourceMaps) {
188
+ commons_1.Contract.require(instrumentedSources.length === finalSourceMaps.length, 'Assuming alignment of source code and source map arrays.');
189
+ if (inputBundle.type === 'gwt') {
190
+ commons_1.Contract.require(instrumentedSources.length === 1, 'Assuming only one code fragment to be passed as argument.');
191
+ const processedCodeString = JSON.stringify(`${this.vaccineSource} ${instrumentedSources[0]}`);
192
+ if (inputBundle.codeAsArrayArgument) {
193
+ writeToFile(toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${inputBundle.functionName}([${processedCodeString}]);`);
194
+ }
195
+ else {
196
+ writeToFile(toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${inputBundle.functionName}(${processedCodeString});`);
197
+ }
198
+ }
199
+ else {
200
+ commons_1.Contract.require(instrumentedSources.length === 1, 'Assuming only one code fragment to be passed as argument for JavaScript bundles.');
201
+ writeToFile(toFile, `${exports.IS_INSTRUMENTED_TOKEN} ${this.vaccineSource} ${instrumentedSources[0]} \n${finalSourceMaps[0]}`);
202
+ }
203
+ }
155
204
  async removeUnwantedInstrumentation(taskElement, instrumentedSource, configurationAlternative, sourcePattern, instrumentedSourcemap) {
156
205
  const instrumentedSourceMapConsumer = await new source_map_1.SourceMapConsumer(instrumentedSourcemap);
157
206
  // Without a source map, excludes/includes do not work.
@@ -187,15 +236,19 @@ class IstanbulInstrumenter {
187
236
  *
188
237
  * @param collector - The collector to send coverage information to.
189
238
  */
190
- loadVaccine(collector) {
239
+ loadVaccine(filePath, collector) {
191
240
  // We first replace parameters in the file with the
192
241
  // actual values, for example, the collector to send the coverage information to.
193
- return fs.readFileSync(this.vaccineFilePath, 'utf8').replace(/\$REPORT_TO_URL/g, collector.url);
242
+ return fs.readFileSync(filePath, 'utf8').replace(/\$REPORT_TO_URL/g, collector.url);
194
243
  }
195
244
  /**
196
245
  * @returns whether the given file is supported for instrumentation.
197
246
  */
198
247
  isFileTypeSupported(fileName) {
248
+ if (fileName.endsWith('.devmode.js') || fileName.endsWith('.nocache.js')) {
249
+ // GWT dev mode files.
250
+ return false;
251
+ }
199
252
  const ext = path.extname(fileName).toLowerCase();
200
253
  return ext === '.js' || ext === '.mjs';
201
254
  }
@@ -250,6 +303,17 @@ async function loadSourceMap(instrumentedSource, instrumentedSourceFileName) {
250
303
  return undefined;
251
304
  }
252
305
  exports.loadSourceMap = loadSourceMap;
306
+ function loadInputSourceMaps(taskFile, bundleFile, externalSourceMapFile) {
307
+ if ((0, WebToolkit_1.isGwtBundle)(bundleFile)) {
308
+ return (0, WebToolkit_1.loadInputSourceMapsGwt)(taskFile, bundleFile);
309
+ }
310
+ else {
311
+ return loadInputSourceMapsStandard(taskFile, bundleFile, externalSourceMapFile);
312
+ }
313
+ }
314
+ function loadInputSourceMapsStandard(taskFile, bundleFile, externalSourceMapFile) {
315
+ return bundleFile.codeArguments.map(code => loadInputSourceMap(code, taskFile, externalSourceMapFile));
316
+ }
253
317
  /**
254
318
  * Given a source code file, load the corresponding sourcemap.
255
319
  *
@@ -263,7 +327,7 @@ function loadInputSourceMap(inputSource, taskFile, externalSourceMapFile) {
263
327
  if (!(sourceMapOrigin instanceof Task_1.SourceMapFileReference)) {
264
328
  throw new commons_1.IllegalArgumentException('Type of source map not yet supported!');
265
329
  }
266
- return sourceMapFromMapFile(sourceMapOrigin.sourceMapFilePath);
330
+ return (0, FileSystem_1.sourceMapFromMapFile)(sourceMapOrigin.sourceMapFilePath);
267
331
  }
268
332
  else {
269
333
  return sourceMapFromCodeComment(inputSource, taskFile);
@@ -295,7 +359,9 @@ function sourceMapFromCodeComment(sourcecode, sourceFilePath) {
295
359
  }
296
360
  else {
297
361
  result = convertSourceMap
298
- .fromMapFileComment(sourceMapComment, path.dirname(sourceFilePath))
362
+ .fromMapFileComment(sourceMapComment, function (filename) {
363
+ return fs.readFileSync(path.resolve(path.dirname(sourceFilePath), filename), 'utf-8');
364
+ })
299
365
  .toObject();
300
366
  }
301
367
  }
@@ -316,16 +382,6 @@ function sourceMapFromCodeComment(sourcecode, sourceFilePath) {
316
382
  }
317
383
  }
318
384
  exports.sourceMapFromCodeComment = sourceMapFromCodeComment;
319
- /**
320
- * Read a source map from a source map file.
321
- *
322
- * @param mapFilePath
323
- */
324
- function sourceMapFromMapFile(mapFilePath) {
325
- const content = fs.readFileSync(mapFilePath, 'utf8');
326
- return JSON.parse(content);
327
- }
328
- exports.sourceMapFromMapFile = sourceMapFromMapFile;
329
385
  function writeToFile(filePath, fileContent) {
330
386
  mkdirp.sync(path.dirname(filePath));
331
387
  fs.writeFileSync(filePath, fileContent);
@@ -1 +1 @@
1
- {"version":3,"file":"Postprocessor.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Postprocessor.ts"],"names":[],"mappings":"AAGA,OAAO,EAON,cAAc,EAUd,MAAM,cAAc,CAAC;AA4LtB;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,GAClD,MAAM,CAkBR"}
1
+ {"version":3,"file":"Postprocessor.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Postprocessor.ts"],"names":[],"mappings":"AAGA,OAAO,EAON,cAAc,EAYd,MAAM,cAAc,CAAC;AA4LtB;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,GAClD,MAAM,CAkBR"}
@@ -4,6 +4,30 @@ import { Optional } from 'typescript-optional';
4
4
  */
5
5
  export declare abstract class SourceMapReference {
6
6
  }
7
+ type BaseBundle = {
8
+ content: string;
9
+ codeArguments: string[];
10
+ };
11
+ /**
12
+ * A standard JavaScript bundle. Produced, for example, with bundlers
13
+ * like Webpack or Vite.
14
+ */
15
+ export type StandardBundle = BaseBundle & {
16
+ type: 'javascript';
17
+ };
18
+ /**
19
+ * A Google Web-Toolkit Js bundle file.
20
+ */
21
+ export type GwtBundle = BaseBundle & {
22
+ type: 'gwt';
23
+ functionName: string;
24
+ fragmentId: string;
25
+ codeAsArrayArgument: boolean;
26
+ };
27
+ /**
28
+ * A bundle to be handled by the instrumenter.
29
+ */
30
+ export type Bundle = BaseBundle & (StandardBundle | GwtBundle);
7
31
  /**
8
32
  * One element of an instrumentation task.
9
33
  * It corresponds to instrumenting a single file.
@@ -156,4 +180,5 @@ export declare class SourceMapFileReference extends SourceMapReference {
156
180
  readonly sourceMapFilePath: string;
157
181
  constructor(sourceMapFilePath: string);
158
182
  }
183
+ export {};
159
184
  //# sourceMappingURL=Task.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Task.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/Task.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Bundle, GwtBundle } from './Task';
2
+ import { RawSourceMap } from 'source-map';
3
+ /**
4
+ * Information on a GWT function call, typically with code to be evaluated as arguments.
5
+ */
6
+ export type GwtCallInfos = {
7
+ codeArguments: string[];
8
+ functionName: string;
9
+ codeAsArrayArgument: boolean;
10
+ };
11
+ /**
12
+ * There are different places where a 'symbolMaps' folder can be:
13
+ * (1) within the `WEB-INF` folder (`WEB-INF/deploy/<module-name>/symbolMaps`)
14
+ * or (2) it can be a sibling of the parent `deferredjs` folder.
15
+ *
16
+ * @param taskFile - Path to the JS bundle file to start searching from.
17
+ */
18
+ export declare function determineSymbolMapsDir(taskFile: string): string[];
19
+ /**
20
+ * Extract the GWT function calls from the GWT bundle. These function calls
21
+ * do have the actual application code as arguments.
22
+ *
23
+ * Examples of `bundleContent` (without the double quotes):
24
+ * "showcase.onScriptDownloaded(["var $wnd = ..... __gwtModuleFunction.__moduleStartupDone($gwt.permProps);\n//# sourceURL=showcase-0.js\n"]);"
25
+ * "$wnd.showcase.runAsyncCallback3("function bc(a){Wb((Ze(),Xe),a) ..... nZ5b(El)(3);\n//# sourceURL=showcase-3.js\n")
26
+ */
27
+ export declare function extractGwtCallInfos(bundleContent: string): GwtCallInfos | null;
28
+ /**
29
+ * Load the source map for the given GWT bundle.
30
+ */
31
+ export declare function loadInputSourceMapsGwt(taskFile: string, bundleFile: GwtBundle): Array<RawSourceMap | undefined>;
32
+ /**
33
+ * Determine the ID of the given GWT bundle file.
34
+ */
35
+ export declare function determineGwtFileUid(filename: string): string | undefined;
36
+ /**
37
+ * Is the given bundle a GWT bundle?
38
+ */
39
+ export declare function isGwtBundle(bundle: Bundle): bundle is GwtBundle;
40
+ //# sourceMappingURL=WebToolkit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebToolkit.d.ts","sourceRoot":"","sources":["../../../src/instrumenter/WebToolkit.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3G;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBjE;AAkBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA+B9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,CAAC,YAAY,GAAG,SAAS,CAAC,CAgC/G;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,SAAS,CAE/D"}
@@ -0,0 +1,147 @@
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.isGwtBundle = exports.determineGwtFileUid = exports.loadInputSourceMapsGwt = exports.extractGwtCallInfos = exports.determineSymbolMapsDir = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const FileSystem_1 = require("./FileSystem");
9
+ const types_1 = require("@babel/types");
10
+ const parser_1 = require("@babel/parser");
11
+ const commons_1 = require("@cqse/commons");
12
+ /**
13
+ * There are different places where a 'symbolMaps' folder can be:
14
+ * (1) within the `WEB-INF` folder (`WEB-INF/deploy/<module-name>/symbolMaps`)
15
+ * or (2) it can be a sibling of the parent `deferredjs` folder.
16
+ *
17
+ * @param taskFile - Path to the JS bundle file to start searching from.
18
+ */
19
+ function determineSymbolMapsDir(taskFile) {
20
+ const symbolMapDirs = [];
21
+ let webInfDir = null;
22
+ const pathComponents = path_1.default.resolve(taskFile).split(path_1.default.sep);
23
+ for (let i = pathComponents.length - 2; i >= 0; i--) {
24
+ const fullDirPath = pathComponents.slice(0, i).join(path_1.default.sep);
25
+ const webInfDirCandidate = path_1.default.join(fullDirPath, 'WEB-INF');
26
+ if ((0, FileSystem_1.isExistingDirectory)(webInfDirCandidate)) {
27
+ webInfDir = webInfDirCandidate;
28
+ }
29
+ const symbolMapDirCandidate = path_1.default.join(fullDirPath, 'symbolMaps');
30
+ if ((0, FileSystem_1.isExistingDirectory)(symbolMapDirCandidate)) {
31
+ symbolMapDirs.push(symbolMapDirCandidate);
32
+ }
33
+ }
34
+ if (webInfDir != null) {
35
+ (0, FileSystem_1.findSubFolders)(webInfDir, 'symbolMaps').forEach(dir => symbolMapDirs.push(dir));
36
+ }
37
+ return symbolMapDirs;
38
+ }
39
+ exports.determineSymbolMapsDir = determineSymbolMapsDir;
40
+ function expressionToString(expression) {
41
+ if ((0, types_1.isMemberExpression)(expression) && (0, types_1.isExpression)(expression.property)) {
42
+ return `${expressionToString(expression.object)}.${expressionToString(expression.property)}`;
43
+ }
44
+ else if ((0, types_1.isIdentifier)(expression)) {
45
+ return expression.name;
46
+ }
47
+ throw new commons_1.IllegalArgumentException('Type of expression not yet supported.');
48
+ }
49
+ function extractQualifiedFunctionName(call) {
50
+ if ((0, types_1.isMemberExpression)(call.callee) || (0, types_1.isIdentifier)(call.callee)) {
51
+ return expressionToString(call.callee);
52
+ }
53
+ throw new commons_1.IllegalArgumentException('Type of callee not yet supported.');
54
+ }
55
+ /**
56
+ * Extract the GWT function calls from the GWT bundle. These function calls
57
+ * do have the actual application code as arguments.
58
+ *
59
+ * Examples of `bundleContent` (without the double quotes):
60
+ * "showcase.onScriptDownloaded(["var $wnd = ..... __gwtModuleFunction.__moduleStartupDone($gwt.permProps);\n//# sourceURL=showcase-0.js\n"]);"
61
+ * "$wnd.showcase.runAsyncCallback3("function bc(a){Wb((Ze(),Xe),a) ..... nZ5b(El)(3);\n//# sourceURL=showcase-3.js\n")
62
+ */
63
+ function extractGwtCallInfos(bundleContent) {
64
+ const ast = (0, parser_1.parse)(bundleContent);
65
+ if (ast.program.body.length === 0) {
66
+ return null;
67
+ }
68
+ if (!(0, types_1.isExpressionStatement)(ast.program.body[0])) {
69
+ return null;
70
+ }
71
+ const call = ast.program.body[0].expression;
72
+ if (!(0, types_1.isCallExpression)(call)) {
73
+ return null;
74
+ }
75
+ const qualifiedFunctionName = extractQualifiedFunctionName(call);
76
+ const firstArgument = call.arguments[0];
77
+ if ((0, types_1.isArrayExpression)(firstArgument)) {
78
+ const codeArguments = [];
79
+ for (const element of firstArgument.elements) {
80
+ if ((0, types_1.isStringLiteral)(element)) {
81
+ codeArguments.push(element.value);
82
+ }
83
+ else {
84
+ throw new Error(`Did expect only string arguments in the call of "${qualifiedFunctionName}".`);
85
+ }
86
+ }
87
+ return { codeArguments, functionName: qualifiedFunctionName, codeAsArrayArgument: true };
88
+ }
89
+ else if ((0, types_1.isStringLiteral)(firstArgument)) {
90
+ const codeArgument = firstArgument.value;
91
+ return { codeArguments: [codeArgument], functionName: qualifiedFunctionName, codeAsArrayArgument: false };
92
+ }
93
+ return null;
94
+ }
95
+ exports.extractGwtCallInfos = extractGwtCallInfos;
96
+ /**
97
+ * Load the source map for the given GWT bundle.
98
+ */
99
+ function loadInputSourceMapsGwt(taskFile, bundleFile) {
100
+ // taskFile:
101
+ // war/stockwatcher/E2C1FB09E006E0A2420123D036967150.cache.js
102
+ // war/showcase/deferredjs/28F63AD125178AAAB80993C11635D26F/5.cache.js
103
+ const mapDirs = determineSymbolMapsDir(taskFile);
104
+ const fileNumberMatcher = /sourceURL=(.*)-(\d+).js(\\n)*\s*$/;
105
+ const mapModules = bundleFile.codeArguments.map(code => {
106
+ const matches = fileNumberMatcher.exec(code);
107
+ if (!matches) {
108
+ return ['', -1];
109
+ }
110
+ return [matches[1], Number.parseInt(matches[2])];
111
+ });
112
+ const sourceMapFiles = mapModules.map(module => {
113
+ for (const mapDir of mapDirs) {
114
+ const mapFileCandidate = `${mapDir}/${bundleFile.fragmentId}_sourceMap${module[1]}.json`;
115
+ if ((0, FileSystem_1.isExistingFile)(mapFileCandidate)) {
116
+ return mapFileCandidate;
117
+ }
118
+ }
119
+ return undefined;
120
+ });
121
+ return sourceMapFiles.map(file => {
122
+ if (file) {
123
+ return (0, FileSystem_1.sourceMapFromMapFile)(file);
124
+ }
125
+ return undefined;
126
+ });
127
+ }
128
+ exports.loadInputSourceMapsGwt = loadInputSourceMapsGwt;
129
+ /**
130
+ * Determine the ID of the given GWT bundle file.
131
+ */
132
+ function determineGwtFileUid(filename) {
133
+ const fileUidMatcher = /.*([0-9A-Fa-f]{32}).*/;
134
+ const uidMatches = fileUidMatcher.exec(filename);
135
+ if (!uidMatches || uidMatches.length < 2) {
136
+ return undefined;
137
+ }
138
+ return uidMatches[1];
139
+ }
140
+ exports.determineGwtFileUid = determineGwtFileUid;
141
+ /**
142
+ * Is the given bundle a GWT bundle?
143
+ */
144
+ function isGwtBundle(bundle) {
145
+ return bundle.type === 'gwt';
146
+ }
147
+ exports.isGwtBundle = isGwtBundle;
package/dist/src/main.js CHANGED
File without changes
package/dist/vaccine.js CHANGED
@@ -1 +1 @@
1
- (()=>{function d(e){let n=new Blob([e],{type:"text/javascript"}),t=URL.createObjectURL(n),s=new Worker(t);return URL.revokeObjectURL(t),s}function h(){return d('var r=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 C=20,m=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(m,()=>this.flush())}addRange(e,t){if(!t.start.line||!t.end.line)return;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>=C&&this.flush()}flush(){this.numberOfCachedPositions!==0&&(this.flushCountdown.stopCountdown(),this.cachedCoveredRanges.forEach((e,t)=>{let o=Array.from(e).map(n=>`${n.start.line}:${n.start.column}:${n.end.line}:${n.end.column}`);this.socket.send(`${"c"} ${t} ${o.join(" ")}`),e.clear()}),this.cachedCoveredRanges.clear(),this.numberOfCachedPositions=0)}};console.log("Starting coverage forwarding worker.");var u=new r("$REPORT_TO_URL/socket"),h=new a(u),f=new Map;onmessage=s=>{if(Array.isArray(s.data))p(s.data);else{let e=s.data;if(e.startsWith("s"))u.send(e);else if(e.startsWith("i")){let t=JSON.parse(e.substring(2));f.set(t.hash,t),console.info(`Received coverage mapping information for "${t.hash}".`)}else e==="unload"?h.flush():console.error(`No handler for message: ${e}`)}};function p(s){var n;let e=s[0],t=s[1],o=f.get(e);if(!o){console.log(`No coverage mapping information for ${e} available!`);return}for(let[c,i]of t.branches.entries()){let l=(n=o.branchMap[c])==null?void 0:n.locations[i];l&&h.addRange(e,l)}for(let c of t.statements){let i=o.statementMap[c];i&&h.addRange(e,i)}}\n')}function c(){return g()}function g(){return window}function p(e,n){let t=c()[e];return t||(t=n,c()[e]=t),t}var l;(function(r){r.MESSAGE_TYPE_SOURCEMAP="s",r.MESSAGE_TYPE_COVERAGE="c",r.ISTANBUL_COV_OBJECT="i",r.UNRESOLVED_CODE_ENTITY="u"})(l||(l={}));function v(e,n){let t=new Map;function s(i){let o=t.get(i);return o||(o={branches:new Map,statements:new Set},t.set(i,o),o)}function r(i,o,w){s(i).branches.set(o,w)}function u(i,o){s(i).statements.add(o)}function a(){n(t),t.clear()}return setInterval(()=>a(),e),{putBranchCoverage:r,putStatementCoverage:u,flush:a}}var C=p("__TS_AGENT",{});function f(){return C._$BcWorker}function S(e){return C._$BcWorker=e,e}var m=v(250,e=>{for(let n of e.entries())f().postMessage(n)});c()._$stmtCov=m.putStatementCoverage;c()._$brCov=m.putBranchCoverage;var b=new Set;c()._$registerCoverageObject=function(e){let n=e.hash;if(b.has(n)){console.log(`Coverage interceptor added twice for ${n}. This seems to be a bug in the instrumentation.`);return}else b.add(n);if(!f()){let t=S(new h);(function(){let r=()=>{m.flush(),t.postMessage("unload")},u=function(i,o){!o||o.addEventListener(i,r,{capture:!0})},a=g();u("blur",a),u("unload",a),u("visibilitychange",a),u("beforeunload",a)})()}(function(){f().postMessage(`${l.ISTANBUL_COV_OBJECT} ${JSON.stringify(e)}`);let s=p("sentMaps",new Set);e.inputSourceMap&&(s.has(e.path)||(f().postMessage(`${l.MESSAGE_TYPE_SOURCEMAP} ${n}:${JSON.stringify(e.inputSourceMap)}`),s.add(e.path)))})()};})();
1
+ "use strict";(()=>{function d(e){let n=new Blob([e],{type:"text/javascript"}),t=URL.createObjectURL(n),r=new Worker(t);return URL.revokeObjectURL(t),r}function f(){return d('var r=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 C=20,m=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(m,()=>this.flush())}addRange(e,t){if(!t.start.line||!t.end.line)return;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>=C&&this.flush()}flush(){this.numberOfCachedPositions!==0&&(this.flushCountdown.stopCountdown(),this.cachedCoveredRanges.forEach((e,t)=>{let o=Array.from(e).map(n=>`${n.start.line}:${n.start.column}:${n.end.line}:${n.end.column}`);this.socket.send(`${"c"} ${t} ${o.join(" ")}`),e.clear()}),this.cachedCoveredRanges.clear(),this.numberOfCachedPositions=0)}};console.log("Starting coverage forwarding worker.");var u=new r("$REPORT_TO_URL/socket"),h=new a(u),f=new Map;onmessage=s=>{if(Array.isArray(s.data))p(s.data);else{let e=s.data;if(e.startsWith("s"))u.send(e);else if(e.startsWith("i")){let t=JSON.parse(e.substring(2));f.set(t.hash,t),console.info(`Received coverage mapping information for "${t.hash}".`)}else e==="unload"?h.flush():console.error(`No handler for message: ${e}`)}};function p(s){var n;let e=s[0],t=s[1],o=f.get(e);if(!o){console.log(`No coverage mapping information for ${e} available!`);return}for(let[c,i]of t.branches.entries()){let l=(n=o.branchMap[c])==null?void 0:n.locations[i];l&&h.addRange(e,l)}for(let c of t.statements){let i=o.statementMap[c];i&&h.addRange(e,i)}}\n')}function u(){return h()}function h(){return window}function g(e,n){let t=u()[e];return t||(t=n,u()[e]=t),t}function m(e,n){let t=new Map;function r(s){let o=t.get(s);return o||(o={branches:new Map,statements:new Set},t.set(s,o),o)}function l(s,o,b){r(s).branches.set(o,b)}function a(s,o){r(s).statements.add(o)}function i(){n(t),t.clear()}return setInterval(()=>i(),e),{putBranchCoverage:l,putStatementCoverage:a,flush:i}}var v=g("__TS_AGENT",{});function c(){return v._$BcWorker}function w(e){return v._$BcWorker=e,e}var p=m(250,e=>{for(let n of e.entries())c().postMessage(n)});u()._$stmtCov=p.putStatementCoverage;u()._$brCov=p.putBranchCoverage;var C=new Set;u()._$registerCoverageObject=function(e){let n=e.hash;if(C.has(n)){console.log(`Coverage interceptor added twice for ${n}. This seems to be a bug in the instrumentation.`);return}else C.add(n);if(!c()){let t=w(new f);(function(){let l=()=>{p.flush(),t.postMessage("unload")},a=function(s,o){o&&o.addEventListener(s,l,{capture:!0})},i=h();a("blur",i),a("unload",i),a("visibilitychange",i),a("beforeunload",i)})()}(function(){c().postMessage(`${"i"} ${JSON.stringify(e)}`);let r=g("sentMaps",new Set);e.inputSourceMap&&(r.has(e.path)||(c().postMessage(`${"s"} ${n}:${JSON.stringify(e.inputSourceMap)}`),r.add(e.path)))})()};})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamscale/javascript-instrumenter",
3
- "version": "0.0.1-beta.54",
3
+ "version": "0.0.1-beta.59",
4
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",
@@ -11,63 +11,63 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/cqse/teamscale-javascript-profiler.git"
13
13
  },
14
- "scripts": {
15
- "prepublishOnly": "yarn clean && yarn build",
16
- "clean": "rimraf dist tsconfig.tsbuildinfo",
17
- "build": "tsc --project tsconfig.json && yarn buildVaccine",
18
- "buildVaccine": "node esbuild.mjs",
19
- "instrumenter": "node dist/src/main.js",
20
- "test": "yarn build && NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=8192' jest --forceExit --coverage --silent=true --detectOpenHandles"
21
- },
22
14
  "files": [
23
15
  "dist/**/*"
24
16
  ],
25
17
  "devDependencies": {
26
- "@babel/core": "^7.22.5",
18
+ "@babel/core": "^7.22.8",
27
19
  "@babel/plugin-transform-modules-commonjs": "^7.22.5",
28
- "@babel/preset-env": "^7.22.5",
29
- "@types/async": "^3.2.6",
20
+ "@babel/preset-env": "^7.22.7",
21
+ "@types/argparse": "^2.0.10",
22
+ "@types/async": "^3.2.20",
23
+ "@types/babel__generator": "^7.6.4",
24
+ "@types/babel__traverse": "^7.20.1",
30
25
  "@types/bunyan": "^1.8.8",
31
- "@types/convert-source-map": "^1.5.1",
32
- "@types/glob": "^7.1.3",
26
+ "@types/convert-source-map": "^2.0.0",
27
+ "@types/glob": "^8.1.0",
33
28
  "@types/istanbul-lib-instrument": "^1.7.4",
34
- "@types/jest": "^29.5.1",
35
- "@types/mkdirp": "^1.0.2",
36
- "@types/node": "^15.0.1",
37
- "@types/source-map": "^0.5.7",
38
- "@types/ws": "^7.4.4",
39
- "babel-jest": "^29.5.0",
40
- "esbuild": "^0.13.3",
29
+ "@types/jest": "^29.5.3",
30
+ "@types/node": "^20.4.1",
31
+ "@types/ws": "^8.5.5",
32
+ "babel-jest": "^29.6.1",
33
+ "esbuild": "^0.18.11",
41
34
  "esbuild-plugin-inline-worker": "^0.1.1",
42
- "jest": "^29.5.0",
43
- "rimraf": "^3.0.2",
44
- "ts-jest": "^29.1.0",
45
- "ts-node": "^10.2.1",
46
- "tslib": "^2.2.0",
47
- "typescript": "^4.4.3"
35
+ "jest": "^29.6.1",
36
+ "rimraf": "^5.0.1",
37
+ "ts-jest": "^29.1.1",
38
+ "ts-node": "^10.9.1",
39
+ "tslib": "^2.6.0",
40
+ "typescript": "^5.1.6"
48
41
  },
49
42
  "dependencies": {
50
- "@babel/generator": "^7.22.5",
51
- "@babel/parser": "^7.22.5",
52
- "@babel/traverse": "^7.22.5",
43
+ "@babel/generator": "^7.22.7",
44
+ "@babel/parser": "^7.22.7",
45
+ "@babel/traverse": "^7.22.8",
53
46
  "@babel/types": "^7.22.5",
54
- "@cqse/commons": "0.0.1-beta.52",
55
47
  "@types/micromatch": "^4.0.2",
56
48
  "argparse": "^2.0.1",
57
49
  "async": "^3.2.4",
58
50
  "bunyan": "^1.8.15",
59
- "convert-source-map": "^1.7.0",
60
- "foreground-child": "^2.0.0",
61
- "glob": "^7.1.7",
51
+ "convert-source-map": "^2.0.0",
52
+ "foreground-child": "^3.1.1",
53
+ "glob": "^10.3.3",
62
54
  "istanbul-lib-instrument": "^5.2.1",
63
- "micromatch": "4.0.4",
64
- "mkdirp": "^1.0.4",
55
+ "micromatch": "4.0.5",
56
+ "mkdirp": "^3.0.1",
65
57
  "source-map": "0.7.4",
66
58
  "typescript-optional": "^2.0.1",
67
- "unload": "^2.2.0",
68
- "web-worker": "^1.0.0"
59
+ "unload": "^2.4.1",
60
+ "web-worker": "^1.2.0",
61
+ "@cqse/commons": "0.0.1-beta.59"
69
62
  },
70
63
  "publishConfig": {
71
64
  "access": "public"
65
+ },
66
+ "scripts": {
67
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
68
+ "build": "tsc --project tsconfig.json && pnpm buildVaccine",
69
+ "buildVaccine": "node esbuild.mjs",
70
+ "instrumenter": "node dist/src/main.js",
71
+ "test": "pnpm build && NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=8192' jest --forceExit --coverage --silent=true --detectOpenHandles"
72
72
  }
73
- }
73
+ }