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

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
@@ -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.55",
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/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.55",
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"