@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 +4 -4
- package/dist/package.json +34 -33
- package/dist/src/App.d.ts.map +1 -1
- package/dist/src/App.js +10 -9
- package/dist/src/instrumenter/FileSystem.d.ts +11 -0
- package/dist/src/instrumenter/FileSystem.d.ts.map +1 -1
- package/dist/src/instrumenter/FileSystem.js +32 -3
- package/dist/src/instrumenter/Instrumenter.d.ts +6 -10
- package/dist/src/instrumenter/Instrumenter.d.ts.map +1 -1
- package/dist/src/instrumenter/Instrumenter.js +119 -63
- package/dist/src/instrumenter/Postprocessor.d.ts.map +1 -1
- package/dist/src/instrumenter/Task.d.ts +25 -0
- package/dist/src/instrumenter/Task.d.ts.map +1 -1
- package/dist/src/instrumenter/WebToolkit.d.ts +40 -0
- package/dist/src/instrumenter/WebToolkit.d.ts.map +1 -0
- package/dist/src/instrumenter/WebToolkit.js +147 -0
- package/dist/vaccine.js +1 -1
- package/package.json +34 -33
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 (>=
|
|
20
|
+
NodeJs (>= v16) and pnpm are needed as prerequisites.
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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.
|
|
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": "
|
|
15
|
+
"prepublishOnly": "pnpm clean && pnpm build",
|
|
16
16
|
"clean": "rimraf dist tsconfig.tsbuildinfo",
|
|
17
|
-
"build": "tsc --project tsconfig.json &&
|
|
17
|
+
"build": "tsc --project tsconfig.json && pnpm buildVaccine",
|
|
18
18
|
"buildVaccine": "node esbuild.mjs",
|
|
19
19
|
"instrumenter": "node dist/src/main.js",
|
|
20
|
-
"test": "
|
|
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.
|
|
26
|
+
"@babel/core": "^7.22.8",
|
|
27
27
|
"@babel/plugin-transform-modules-commonjs": "^7.22.5",
|
|
28
|
-
"@babel/preset-env": "^7.22.
|
|
29
|
-
"@types/
|
|
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": "^
|
|
32
|
-
"@types/glob": "^
|
|
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.
|
|
35
|
-
"@types/
|
|
36
|
-
"@types/
|
|
37
|
-
"
|
|
38
|
-
"
|
|
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.
|
|
43
|
-
"rimraf": "^
|
|
44
|
-
"ts-jest": "^29.1.
|
|
45
|
-
"ts-node": "^10.
|
|
46
|
-
"tslib": "^2.
|
|
47
|
-
"typescript": "^
|
|
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.
|
|
51
|
-
"@babel/parser": "^7.22.
|
|
52
|
-
"@babel/traverse": "^7.22.
|
|
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": "
|
|
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": "^
|
|
60
|
-
"foreground-child": "^
|
|
61
|
-
"glob": "^
|
|
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.
|
|
64
|
-
"mkdirp": "^
|
|
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.
|
|
68
|
-
"web-worker": "^1.
|
|
68
|
+
"unload": "^2.4.1",
|
|
69
|
+
"web-worker": "^1.2.0"
|
|
69
70
|
},
|
|
70
71
|
"publishConfig": {
|
|
71
72
|
"access": "public"
|
package/dist/src/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/App.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
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 =
|
|
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(
|
|
55
|
-
logger.debug(
|
|
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.
|
|
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":"
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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,
|
|
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.
|
|
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(
|
|
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(
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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,
|
|
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":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
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),
|
|
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.
|
|
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": "
|
|
15
|
+
"prepublishOnly": "pnpm clean && pnpm build",
|
|
16
16
|
"clean": "rimraf dist tsconfig.tsbuildinfo",
|
|
17
|
-
"build": "tsc --project tsconfig.json &&
|
|
17
|
+
"build": "tsc --project tsconfig.json && pnpm buildVaccine",
|
|
18
18
|
"buildVaccine": "node esbuild.mjs",
|
|
19
19
|
"instrumenter": "node dist/src/main.js",
|
|
20
|
-
"test": "
|
|
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.
|
|
26
|
+
"@babel/core": "^7.22.8",
|
|
27
27
|
"@babel/plugin-transform-modules-commonjs": "^7.22.5",
|
|
28
|
-
"@babel/preset-env": "^7.22.
|
|
29
|
-
"@types/
|
|
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": "^
|
|
32
|
-
"@types/glob": "^
|
|
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.
|
|
35
|
-
"@types/
|
|
36
|
-
"@types/
|
|
37
|
-
"
|
|
38
|
-
"
|
|
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.
|
|
43
|
-
"rimraf": "^
|
|
44
|
-
"ts-jest": "^29.1.
|
|
45
|
-
"ts-node": "^10.
|
|
46
|
-
"tslib": "^2.
|
|
47
|
-
"typescript": "^
|
|
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.
|
|
51
|
-
"@babel/parser": "^7.22.
|
|
52
|
-
"@babel/traverse": "^7.22.
|
|
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": "
|
|
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": "^
|
|
60
|
-
"foreground-child": "^
|
|
61
|
-
"glob": "^
|
|
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.
|
|
64
|
-
"mkdirp": "^
|
|
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.
|
|
68
|
-
"web-worker": "^1.
|
|
68
|
+
"unload": "^2.4.1",
|
|
69
|
+
"web-worker": "^1.2.0"
|
|
69
70
|
},
|
|
70
71
|
"publishConfig": {
|
|
71
72
|
"access": "public"
|