@teamscale/lib-instrument 1.0.0-beta.6 → 1.0.0-beta.7
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/lib/index.d.ts +0 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +0 -5
- package/lib/instrumenter.d.ts +0 -25
- package/lib/instrumenter.d.ts.map +1 -1
- package/lib/instrumenter.js +4 -22
- package/lib/origins.d.ts +0 -20
- package/lib/origins.d.ts.map +1 -1
- package/lib/origins.js +4 -19
- package/lib/utils.d.ts +0 -13
- package/lib/utils.d.ts.map +1 -1
- package/lib/visitor.d.ts +0 -19
- package/lib/visitor.d.ts.map +1 -1
- package/lib/visitor.js +24 -105
- package/package.json +8 -8
package/lib/index.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { Instrumenter, InstrumenterOptions } from './instrumenter';
|
|
2
2
|
export type { InstrumenterOptions } from './instrumenter';
|
|
3
3
|
export { programVisitor } from './visitor';
|
|
4
|
-
/**
|
|
5
|
-
* Creates a new coverage instrumenter.
|
|
6
|
-
*
|
|
7
|
-
* @param opts - instrumenter options
|
|
8
|
-
*/
|
|
9
4
|
export declare function createInstrumenter(opts: InstrumenterOptions): Instrumenter;
|
|
10
5
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,mBAAmB,EAAC,MAAM,gBAAgB,CAAC;AAEjE,YAAY,EAAC,mBAAmB,EAAC,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAC,cAAc,EAAC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,mBAAmB,EAAC,MAAM,gBAAgB,CAAC;AAEjE,YAAY,EAAC,mBAAmB,EAAC,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAC,cAAc,EAAC,MAAM,WAAW,CAAC;AAOzC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,YAAY,CAE1E"}
|
package/lib/index.js
CHANGED
|
@@ -5,11 +5,6 @@ exports.createInstrumenter = createInstrumenter;
|
|
|
5
5
|
const instrumenter_1 = require("./instrumenter");
|
|
6
6
|
var visitor_1 = require("./visitor");
|
|
7
7
|
Object.defineProperty(exports, "programVisitor", { enumerable: true, get: function () { return visitor_1.programVisitor; } });
|
|
8
|
-
/**
|
|
9
|
-
* Creates a new coverage instrumenter.
|
|
10
|
-
*
|
|
11
|
-
* @param opts - instrumenter options
|
|
12
|
-
*/
|
|
13
8
|
function createInstrumenter(opts) {
|
|
14
9
|
return new instrumenter_1.Instrumenter(opts);
|
|
15
10
|
}
|
package/lib/instrumenter.d.ts
CHANGED
|
@@ -3,43 +3,18 @@ import { SourceLocation } from "@babel/types";
|
|
|
3
3
|
import { ParserPlugin as PluginConfig } from '@babel/parser';
|
|
4
4
|
import { RawSourceMap } from "source-map";
|
|
5
5
|
import { InstrumentationOptions } from "./utils";
|
|
6
|
-
/**
|
|
7
|
-
* Options for configuring the coverage instrumenter.
|
|
8
|
-
*/
|
|
9
6
|
export type InstrumenterOptions = InstrumentationOptions & Partial<{
|
|
10
|
-
/** Preserve comments in output */
|
|
11
7
|
preserveComments: boolean;
|
|
12
|
-
/** Generate compact code */
|
|
13
8
|
compact: boolean;
|
|
14
|
-
/** Set to true to instrument ES6 modules */
|
|
15
9
|
esModules: boolean;
|
|
16
|
-
/** Set to true to allow `return` statements outside of functions */
|
|
17
10
|
autoWrap: boolean;
|
|
18
|
-
/** Approach to produce a source map for the instrumented code */
|
|
19
11
|
produceSourceMap: 'none' | 'inline' | 'external';
|
|
20
|
-
/** Turn debugging on */
|
|
21
12
|
debug: boolean;
|
|
22
|
-
/** Set babel parser plugins */
|
|
23
13
|
parserPlugins: PluginConfig[];
|
|
24
14
|
}>;
|
|
25
|
-
/**
|
|
26
|
-
* The main class of the instrumenter.
|
|
27
|
-
*/
|
|
28
15
|
export declare class Instrumenter {
|
|
29
16
|
private readonly opts;
|
|
30
17
|
constructor(opts?: Partial<InstrumenterOptions>);
|
|
31
|
-
/**
|
|
32
|
-
* Instrument the supplied code with coverage statements.
|
|
33
|
-
* To instrument EcmaScript modules, make sure to set the
|
|
34
|
-
* `esModules` option to `true` when creating the instrumenter.
|
|
35
|
-
*
|
|
36
|
-
* @param code - the code to instrument
|
|
37
|
-
* @param filename - the name of the file the code stems from.
|
|
38
|
-
* @param inputSourceMap - the source map that maps the not instrumented code back to its original
|
|
39
|
-
* @param shouldInstrumentCallback - a callback to decide if a given code fragment should be instrumented
|
|
40
|
-
*
|
|
41
|
-
* @returns the instrumented code.
|
|
42
|
-
*/
|
|
43
18
|
instrument(code: string, filename: string | undefined, inputSourceMap: RawSourceMap | undefined, shouldInstrumentCallback?: (path: NodePath, location: SourceLocation) => boolean): Promise<string>;
|
|
44
19
|
}
|
|
45
20
|
//# sourceMappingURL=instrumenter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumenter.d.ts","sourceRoot":"","sources":["../src/instrumenter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,QAAQ,EAAgB,MAAM,aAAa,CAAC;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,YAAY,IAAI,YAAY,EAAC,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAoB,MAAM,YAAY,CAAC;AAG3D,OAAO,EAAC,sBAAsB,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"instrumenter.d.ts","sourceRoot":"","sources":["../src/instrumenter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,QAAQ,EAAgB,MAAM,aAAa,CAAC;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,YAAY,IAAI,YAAY,EAAC,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAoB,MAAM,YAAY,CAAC;AAG3D,OAAO,EAAC,sBAAsB,EAAC,MAAM,SAAS,CAAC;AAK/C,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,GAAG,OAAO,CAAC;IAE/D,gBAAgB,EAAE,OAAO,CAAC;IAG1B,OAAO,EAAE,OAAO,CAAC;IAGjB,SAAS,EAAE,OAAO,CAAC;IAGnB,QAAQ,EAAE,OAAO,CAAC;IAGlB,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;IAGjD,KAAK,EAAE,OAAO,CAAC;IAGf,aAAa,EAAE,YAAY,EAAE,CAAC;CACjC,CAAC,CAAC;AAeH,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAE/B,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC;IAgBzC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,cAAc,EAAE,YAAY,GAAG,SAAS,EACpF,wBAAwB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;CA0DtH"}
|
package/lib/instrumenter.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Instrumenter = void 0;
|
|
4
|
-
/*
|
|
5
|
-
Copyright 2012-2015, Yahoo Inc.
|
|
6
|
-
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
|
|
7
|
-
*/
|
|
8
4
|
const core_1 = require("@babel/core");
|
|
9
5
|
const source_map_1 = require("source-map");
|
|
10
6
|
const visitor_1 = require("./visitor");
|
|
@@ -17,29 +13,15 @@ function mapSourceMapsOption(produceSourceMap) {
|
|
|
17
13
|
}
|
|
18
14
|
return produceSourceMap;
|
|
19
15
|
}
|
|
20
|
-
/**
|
|
21
|
-
* The main class of the instrumenter.
|
|
22
|
-
*/
|
|
23
16
|
class Instrumenter {
|
|
17
|
+
opts;
|
|
24
18
|
constructor(opts) {
|
|
25
19
|
this.opts = { ...opts };
|
|
26
20
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Instrument the supplied code with coverage statements.
|
|
29
|
-
* To instrument EcmaScript modules, make sure to set the
|
|
30
|
-
* `esModules` option to `true` when creating the instrumenter.
|
|
31
|
-
*
|
|
32
|
-
* @param code - the code to instrument
|
|
33
|
-
* @param filename - the name of the file the code stems from.
|
|
34
|
-
* @param inputSourceMap - the source map that maps the not instrumented code back to its original
|
|
35
|
-
* @param shouldInstrumentCallback - a callback to decide if a given code fragment should be instrumented
|
|
36
|
-
*
|
|
37
|
-
* @returns the instrumented code.
|
|
38
|
-
*/
|
|
39
21
|
async instrument(code, filename, inputSourceMap, shouldInstrumentCallback) {
|
|
40
|
-
filename = filename
|
|
22
|
+
filename = filename ?? String(new Date().getTime()) + '.js';
|
|
41
23
|
const { opts } = this;
|
|
42
|
-
const sourceMapToUse = inputSourceMap
|
|
24
|
+
const sourceMapToUse = inputSourceMap ?? opts.inputSourceMap;
|
|
43
25
|
let inputSourceMapConsumer = undefined;
|
|
44
26
|
if (sourceMapToUse) {
|
|
45
27
|
inputSourceMapConsumer = await new source_map_1.SourceMapConsumer(sourceMapToUse);
|
|
@@ -69,7 +51,7 @@ class Instrumenter {
|
|
|
69
51
|
inputSourceMap,
|
|
70
52
|
isInstrumentedToken: opts.isInstrumentedToken,
|
|
71
53
|
codeToPrepend: opts.codeToPrepend,
|
|
72
|
-
shouldInstrumentCallback: shouldInstrumentCallback
|
|
54
|
+
shouldInstrumentCallback: shouldInstrumentCallback ?? opts.shouldInstrumentCallback
|
|
73
55
|
});
|
|
74
56
|
return {
|
|
75
57
|
visitor: {
|
package/lib/origins.d.ts
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
import { SourceLocation } from "@babel/types";
|
|
2
2
|
import { SourceMapConsumer } from "source-map";
|
|
3
|
-
/**
|
|
4
|
-
* Generator for identifiers that are unique across files to instrument.
|
|
5
|
-
* Relevant in case no Ecmascript modules are used.
|
|
6
|
-
*
|
|
7
|
-
* We assume that the files to be executed in a browser can
|
|
8
|
-
* stem from different runs of the instrumenter. We have to decrease
|
|
9
|
-
* the probability of colliding identifiers.
|
|
10
|
-
*/
|
|
11
3
|
export declare const fileIdSeqGenerator: {
|
|
12
4
|
next: () => string;
|
|
13
5
|
};
|
|
14
|
-
/**
|
|
15
|
-
* Mapping source locations to their origins, before the last transpilation,
|
|
16
|
-
* based on the source map.
|
|
17
|
-
*/
|
|
18
6
|
export declare class SourceOrigins {
|
|
19
7
|
private readonly sourceMap?;
|
|
20
|
-
/**
|
|
21
|
-
* The mapping of file ids to the file names in the transpiler origin,
|
|
22
|
-
* that is, the file names found in the source map.
|
|
23
|
-
*/
|
|
24
8
|
readonly originToIdMap: Map<string, string>;
|
|
25
9
|
constructor(sourceMap: SourceMapConsumer | undefined);
|
|
26
|
-
/**
|
|
27
|
-
* Register source origin file and retrieve a unique identifier for it; furthermore, map
|
|
28
|
-
* the given location to the location in the origin.
|
|
29
|
-
*/
|
|
30
10
|
ensureKnownOrigin(loc: SourceLocation): [string, SourceLocation];
|
|
31
11
|
}
|
|
32
12
|
//# sourceMappingURL=origins.d.ts.map
|
package/lib/origins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../src/origins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAyB,iBAAiB,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../src/origins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAyB,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAUrE,eAAO,MAAM,kBAAkB,EAAE;IAAE,IAAI,EAAE,MAAM,MAAM,CAAA;CAkBjD,CAAC;AAML,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAoB;IAM/C,SAAgB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAEvC,SAAS,EAAE,iBAAiB,GAAG,SAAS;IASpD,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;CA6BnE"}
|
package/lib/origins.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SourceOrigins = exports.fileIdSeqGenerator = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Generator for identifiers that are unique across files to instrument.
|
|
6
|
-
* Relevant in case no Ecmascript modules are used.
|
|
7
|
-
*
|
|
8
|
-
* We assume that the files to be executed in a browser can
|
|
9
|
-
* stem from different runs of the instrumenter. We have to decrease
|
|
10
|
-
* the probability of colliding identifiers.
|
|
11
|
-
*/
|
|
12
4
|
exports.fileIdSeqGenerator = (() => {
|
|
13
5
|
const instrumenterRunId = process.pid;
|
|
14
6
|
let fileIdSeq = 0;
|
|
@@ -29,28 +21,21 @@ exports.fileIdSeqGenerator = (() => {
|
|
|
29
21
|
}
|
|
30
22
|
};
|
|
31
23
|
})();
|
|
32
|
-
/**
|
|
33
|
-
* Mapping source locations to their origins, before the last transpilation,
|
|
34
|
-
* based on the source map.
|
|
35
|
-
*/
|
|
36
24
|
class SourceOrigins {
|
|
25
|
+
sourceMap;
|
|
26
|
+
originToIdMap;
|
|
37
27
|
constructor(sourceMap) {
|
|
38
28
|
this.originToIdMap = new Map();
|
|
39
29
|
this.sourceMap = sourceMap;
|
|
40
30
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Register source origin file and retrieve a unique identifier for it; furthermore, map
|
|
43
|
-
* the given location to the location in the origin.
|
|
44
|
-
*/
|
|
45
31
|
ensureKnownOrigin(loc) {
|
|
46
|
-
var _a, _b;
|
|
47
32
|
let startPos = undefined;
|
|
48
33
|
let endPos = undefined;
|
|
49
|
-
let filename =
|
|
34
|
+
let filename = loc.filename ?? '';
|
|
50
35
|
if (this.sourceMap) {
|
|
51
36
|
startPos = this.sourceMap.originalPositionFor({ line: loc.start.line, column: loc.start.column });
|
|
52
37
|
endPos = this.sourceMap.originalPositionFor({ line: loc.end.line, column: loc.end.column });
|
|
53
|
-
filename =
|
|
38
|
+
filename = startPos.source ?? loc.filename;
|
|
54
39
|
}
|
|
55
40
|
if (!startPos || !endPos) {
|
|
56
41
|
startPos = { line: loc.start.line, column: loc.start.column, source: null, name: null };
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
import { SourceLocation } from "@babel/types";
|
|
2
2
|
import { RawSourceMap } from "source-map";
|
|
3
3
|
import { NodePath } from "@babel/core";
|
|
4
|
-
/**
|
|
5
|
-
* Options to configure the instrumenter.
|
|
6
|
-
*/
|
|
7
4
|
export type InstrumentationOptions = Partial<{
|
|
8
|
-
/** Report boolean value of logical expressions */
|
|
9
5
|
reportLogic: boolean;
|
|
10
|
-
/** Use an evaluated function to find coverageGlobalScope */
|
|
11
6
|
coverageGlobalScopeFunc: boolean;
|
|
12
|
-
/** Names of methods to ignore by default on classes */
|
|
13
7
|
ignoreClassMethods: string[];
|
|
14
|
-
/** The input source map, that maps the uninstrumented code back to the original code */
|
|
15
8
|
inputSourceMap?: RawSourceMap;
|
|
16
|
-
/** Token to add in the very beginning to indicate that the instrumentation has been performed */
|
|
17
9
|
isInstrumentedToken?: string;
|
|
18
|
-
/** Code to add before the instrumented input code */
|
|
19
10
|
codeToPrepend?: string;
|
|
20
|
-
/** Callback for determining if a given code fragment should be instrument */
|
|
21
11
|
shouldInstrumentCallback?: (path: NodePath, loc: SourceLocation) => boolean;
|
|
22
12
|
}>;
|
|
23
|
-
/**
|
|
24
|
-
* Source code fragment within on file.
|
|
25
|
-
*/
|
|
26
13
|
export type CodeRange = {
|
|
27
14
|
start: {
|
|
28
15
|
line?: number;
|
package/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AACxC,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AACxC,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAKrC,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC;IAEzC,WAAW,EAAE,OAAO,CAAC;IAGrB,uBAAuB,EAAE,OAAO,CAAC;IAGjC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAG7B,cAAc,CAAC,EAAE,YAAY,CAAC;IAG9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAG7B,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,wBAAwB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;CAC/E,CAAC,CAAC;AAKH,MAAM,MAAM,SAAS,GAAG;IACpB,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,GAAG,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C,CAAC"}
|
package/lib/visitor.d.ts
CHANGED
|
@@ -3,25 +3,6 @@ import { Program } from "@babel/types";
|
|
|
3
3
|
type BabelTypes = typeof import("@babel/types");
|
|
4
4
|
import { SourceMapConsumer } from "source-map";
|
|
5
5
|
import { InstrumentationOptions } from "./utils";
|
|
6
|
-
/**
|
|
7
|
-
* `programVisitor` is a `babel` adaptor for instrumentation.
|
|
8
|
-
*
|
|
9
|
-
* It returns an object with two methods `enter` and `exit`.
|
|
10
|
-
* These should be assigned to or called from `Program` entry and exit functions
|
|
11
|
-
* in a babel visitor.
|
|
12
|
-
*
|
|
13
|
-
* These functions do not make assumptions about the state set by Babel and thus
|
|
14
|
-
* can be used in a context other than a Babel plugin.
|
|
15
|
-
*
|
|
16
|
-
* The exit function returns an object that currently has the following keys:
|
|
17
|
-
*
|
|
18
|
-
* `fileCoverage` - the file coverage object created for the source file.
|
|
19
|
-
* `sourceMappingURL` - any source mapping URL found when processing the file.
|
|
20
|
-
*
|
|
21
|
-
* @param types - an instance of babel-types.
|
|
22
|
-
* @param inputSourceMapConsumer - access object for the source map of the input.
|
|
23
|
-
* @param opts - additional options.
|
|
24
|
-
*/
|
|
25
6
|
export declare function programVisitor(types: BabelTypes, inputSourceMapConsumer: SourceMapConsumer | undefined, opts: InstrumentationOptions): {
|
|
26
7
|
enter(path: NodePath<Program>): void;
|
|
27
8
|
exit(path: NodePath<Program>): void;
|
package/lib/visitor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../src/visitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,QAAQ,EAAQ,MAAM,aAAa,CAAC;AAElD,OAAO,EAK8C,OAAO,EAE3D,MAAM,cAAc,CAAC;AAEtB,KAAK,UAAU,GAAG,cAAc,cAAc,CAAC,CAAA;AAE/C,OAAO,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAC,sBAAsB,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../src/visitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,QAAQ,EAAQ,MAAM,aAAa,CAAC;AAElD,OAAO,EAK8C,OAAO,EAE3D,MAAM,cAAc,CAAC;AAEtB,KAAK,UAAU,GAAG,cAAc,cAAc,CAAC,CAAA;AAE/C,OAAO,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAC,sBAAsB,EAAC,MAAM,SAAS,CAAC;AAwnB/C,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EACjB,sBAAsB,EAAE,iBAAiB,GAAG,SAAS,EACrD,IAAI,EAAE,sBAAsB;gBAYvC,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI;eASzB,QAAQ,CAAC,OAAO,CAAC;EA+BnC"}
|
package/lib/visitor.js
CHANGED
|
@@ -3,15 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.programVisitor = programVisitor;
|
|
4
4
|
const core_1 = require("@babel/core");
|
|
5
5
|
const origins_1 = require("./origins");
|
|
6
|
-
// Pattern for istanbul to ignore a section
|
|
7
6
|
const COMMENT_RE = /^\s*istanbul\s+ignore\s+(if|else|next)(?=\W|$)/;
|
|
8
|
-
// Pattern for istanbul to ignore the whole file
|
|
9
7
|
const COMMENT_FILE_RE = /^\s*istanbul\s+ignore\s+(file)(?=\W|$)/;
|
|
10
|
-
/**
|
|
11
|
-
* `VisitState` holds the state of the visitor, provides helper functions
|
|
12
|
-
* and is the `this` for the individual coverage visitors.
|
|
13
|
-
*/
|
|
14
8
|
class VisitState {
|
|
9
|
+
types;
|
|
10
|
+
attrs;
|
|
11
|
+
nextIgnore;
|
|
12
|
+
ignoreClassMethods;
|
|
13
|
+
reportLogic;
|
|
14
|
+
shouldInstrumentCallback;
|
|
15
|
+
origins;
|
|
15
16
|
constructor(types, inputSourceMapConsumer, ignoreClassMethods = [], reportLogic = false, shouldInstrumentCallback) {
|
|
16
17
|
this.attrs = {};
|
|
17
18
|
this.nextIgnore = null;
|
|
@@ -21,27 +22,21 @@ class VisitState {
|
|
|
21
22
|
this.shouldInstrumentCallback = shouldInstrumentCallback;
|
|
22
23
|
this.origins = new origins_1.SourceOrigins(inputSourceMapConsumer);
|
|
23
24
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Use the configured callback, if available, to check if the given source
|
|
26
|
-
* location should be instrumented.
|
|
27
|
-
*/
|
|
28
25
|
shouldInstrument(path, loc) {
|
|
29
26
|
if (this.shouldInstrumentCallback) {
|
|
30
27
|
return this.shouldInstrumentCallback(path, loc);
|
|
31
28
|
}
|
|
32
29
|
return true;
|
|
33
30
|
}
|
|
34
|
-
/** Should we ignore the node? Yes, if specifically ignoring or if the node is generated. */
|
|
35
31
|
shouldIgnore(path) {
|
|
36
32
|
return this.nextIgnore !== null || !path.node.loc;
|
|
37
33
|
}
|
|
38
|
-
/** Extract the ignore comment hint (next|if|else) or null. */
|
|
39
34
|
hintFor(node) {
|
|
40
35
|
let hint = null;
|
|
41
36
|
if (node.leadingComments) {
|
|
42
37
|
node.leadingComments.forEach(c => {
|
|
43
38
|
const v = (c.value || '').trim();
|
|
44
|
-
const groups =
|
|
39
|
+
const groups = COMMENT_RE.exec(v);
|
|
45
40
|
if (groups) {
|
|
46
41
|
hint = groups[1];
|
|
47
42
|
}
|
|
@@ -49,35 +44,26 @@ class VisitState {
|
|
|
49
44
|
}
|
|
50
45
|
return hint;
|
|
51
46
|
}
|
|
52
|
-
/**
|
|
53
|
-
* For these expressions the statement counter needs to be hoisted, so
|
|
54
|
-
* function name inference can be preserved.
|
|
55
|
-
*/
|
|
56
47
|
counterNeedsHoisting(path) {
|
|
57
48
|
return (path.isFunctionExpression() ||
|
|
58
49
|
path.isArrowFunctionExpression() ||
|
|
59
50
|
path.isClassExpression());
|
|
60
51
|
}
|
|
61
|
-
/** All the generic stuff that needs to be done on enter for every node. */
|
|
62
52
|
onEnter(path) {
|
|
63
53
|
const n = path.node;
|
|
64
|
-
// if already ignoring, nothing more to do
|
|
65
54
|
if (this.nextIgnore !== null) {
|
|
66
55
|
return;
|
|
67
56
|
}
|
|
68
|
-
// check hint to see if ignore should be turned on
|
|
69
57
|
const hint = this.hintFor(n);
|
|
70
58
|
if (hint === 'next') {
|
|
71
59
|
this.nextIgnore = n;
|
|
72
60
|
return;
|
|
73
61
|
}
|
|
74
|
-
// else check custom node attribute set by a prior visitor
|
|
75
62
|
if (this.getAttr(path.node, 'skip-all') !== null) {
|
|
76
63
|
this.nextIgnore = n;
|
|
77
64
|
}
|
|
78
|
-
// else check for ignored class methods
|
|
79
65
|
if (path.isFunctionExpression() &&
|
|
80
|
-
this.ignoreClassMethods.some(name =>
|
|
66
|
+
this.ignoreClassMethods.some(name => name === path.node.id?.name)) {
|
|
81
67
|
this.nextIgnore = n;
|
|
82
68
|
return;
|
|
83
69
|
}
|
|
@@ -87,23 +73,16 @@ class VisitState {
|
|
|
87
73
|
return;
|
|
88
74
|
}
|
|
89
75
|
}
|
|
90
|
-
/**
|
|
91
|
-
* All the generic stuff on exit of a node, including resetting ignores and custom node attrs.
|
|
92
|
-
*/
|
|
93
76
|
onExit(path) {
|
|
94
|
-
// restore ignore status, if needed
|
|
95
77
|
if (path.node === this.nextIgnore) {
|
|
96
78
|
this.nextIgnore = null;
|
|
97
79
|
}
|
|
98
|
-
// nuke all attributes for the node
|
|
99
80
|
delete path.node.__cov__;
|
|
100
81
|
}
|
|
101
|
-
/** Set a node attribute for the supplied node. */
|
|
102
82
|
setAttr(node, name, value) {
|
|
103
83
|
node.__cov__ = node.__cov__ || {};
|
|
104
84
|
node.__cov__[name] = value;
|
|
105
85
|
}
|
|
106
|
-
/** Retrieve a node attribute for the supplied node or null. */
|
|
107
86
|
getAttr(node, name) {
|
|
108
87
|
const c = node.__cov__;
|
|
109
88
|
if (!c) {
|
|
@@ -112,7 +91,6 @@ class VisitState {
|
|
|
112
91
|
return c[name];
|
|
113
92
|
}
|
|
114
93
|
insertCounter(path, increment) {
|
|
115
|
-
var _a, _b;
|
|
116
94
|
const T = this.types;
|
|
117
95
|
if (path.isBlockStatement()) {
|
|
118
96
|
path.node.body.unshift(T.expressionStatement(increment));
|
|
@@ -122,11 +100,9 @@ class VisitState {
|
|
|
122
100
|
}
|
|
123
101
|
else if (this.counterNeedsHoisting(path) &&
|
|
124
102
|
T.isVariableDeclarator(path.parent)) {
|
|
125
|
-
|
|
126
|
-
// function names are maintained.
|
|
127
|
-
const grandParentPath = (_a = path.parentPath) === null || _a === void 0 ? void 0 : _a.parentPath;
|
|
103
|
+
const grandParentPath = path.parentPath?.parentPath;
|
|
128
104
|
if (grandParentPath && T.isExportNamedDeclaration(grandParentPath.parent)) {
|
|
129
|
-
|
|
105
|
+
grandParentPath.parentPath?.insertBefore(T.expressionStatement(increment));
|
|
130
106
|
}
|
|
131
107
|
else if (grandParentPath &&
|
|
132
108
|
(T.isProgram(grandParentPath.parent) ||
|
|
@@ -145,9 +121,8 @@ class VisitState {
|
|
|
145
121
|
}
|
|
146
122
|
}
|
|
147
123
|
insertFunctionCounter(path) {
|
|
148
|
-
var _a, _b, _c;
|
|
149
124
|
const T = this.types;
|
|
150
|
-
if (!(
|
|
125
|
+
if (!(path.node?.loc)) {
|
|
151
126
|
return;
|
|
152
127
|
}
|
|
153
128
|
const n = path.node;
|
|
@@ -156,23 +131,21 @@ class VisitState {
|
|
|
156
131
|
case 'FunctionDeclaration':
|
|
157
132
|
case 'FunctionExpression':
|
|
158
133
|
if (n.id) {
|
|
159
|
-
declarationLocation =
|
|
134
|
+
declarationLocation = n.id.loc ?? undefined;
|
|
160
135
|
}
|
|
161
136
|
break;
|
|
162
137
|
}
|
|
163
138
|
const body = path.get('body');
|
|
164
|
-
const loc =
|
|
139
|
+
const loc = path.node.loc ?? declarationLocation;
|
|
165
140
|
const [originFileId, originPos] = this.origins.ensureKnownOrigin(loc);
|
|
166
141
|
if (body.isBlockStatement() && this.shouldInstrument(path, originPos)) {
|
|
167
|
-
// For functions, we only cover the first line of its body.
|
|
168
142
|
originPos.end = originPos.start;
|
|
169
143
|
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
170
144
|
body.node.body.unshift(T.expressionStatement(increment));
|
|
171
145
|
}
|
|
172
146
|
}
|
|
173
147
|
insertStatementCounter(path) {
|
|
174
|
-
|
|
175
|
-
const loc = (_a = path.node) === null || _a === void 0 ? void 0 : _a.loc;
|
|
148
|
+
const loc = path.node?.loc;
|
|
176
149
|
if (!loc) {
|
|
177
150
|
return;
|
|
178
151
|
}
|
|
@@ -184,7 +157,7 @@ class VisitState {
|
|
|
184
157
|
this.insertCounter(path, increment);
|
|
185
158
|
}
|
|
186
159
|
insertBranchCounter(path, loc) {
|
|
187
|
-
loc = loc
|
|
160
|
+
loc = loc ?? path.node.loc;
|
|
188
161
|
if (!loc) {
|
|
189
162
|
return;
|
|
190
163
|
}
|
|
@@ -214,16 +187,11 @@ class VisitState {
|
|
|
214
187
|
}
|
|
215
188
|
}
|
|
216
189
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Create a line coverage reporting statement node.
|
|
219
|
-
*/
|
|
220
190
|
function newLineCoverageExpression(originFileId, range) {
|
|
221
191
|
const argumentList = [
|
|
222
192
|
{ type: 'Identifier', name: originFileId },
|
|
223
193
|
{ type: 'NumericLiteral', value: range.start.line }
|
|
224
194
|
];
|
|
225
|
-
// Only pass end line argument if they are different.
|
|
226
|
-
// See also https://v8.dev/blog/adaptor-frame for performance considerations
|
|
227
195
|
if (range.start.line !== range.end.line) {
|
|
228
196
|
argumentList.push({ type: 'NumericLiteral', value: range.end.line });
|
|
229
197
|
}
|
|
@@ -233,9 +201,6 @@ function newLineCoverageExpression(originFileId, range) {
|
|
|
233
201
|
arguments: argumentList
|
|
234
202
|
};
|
|
235
203
|
}
|
|
236
|
-
/**
|
|
237
|
-
* Creates a new string constant AST node.
|
|
238
|
-
*/
|
|
239
204
|
function newStringConstDeclarationNode(name, value) {
|
|
240
205
|
return {
|
|
241
206
|
type: 'VariableDeclaration',
|
|
@@ -255,18 +220,7 @@ function newStringConstDeclarationNode(name, value) {
|
|
|
255
220
|
]
|
|
256
221
|
};
|
|
257
222
|
}
|
|
258
|
-
/**
|
|
259
|
-
* Generic function that takes a set of visitor methods and
|
|
260
|
-
* returns a visitor object with `enter` and `exit` properties,
|
|
261
|
-
* such that:
|
|
262
|
-
*
|
|
263
|
-
* - standard entry processing is done
|
|
264
|
-
* - the supplied visitors are called only when ignore is not in effect;
|
|
265
|
-
* it reliefs them from worrying about ignore states and generated nodes.
|
|
266
|
-
* - standard exit processing is done
|
|
267
|
-
*/
|
|
268
223
|
function entries(...enter) {
|
|
269
|
-
// the enter function
|
|
270
224
|
const wrappedEntry = function (path, node) {
|
|
271
225
|
this.onEnter(path);
|
|
272
226
|
if (this.shouldIgnore(path)) {
|
|
@@ -311,8 +265,6 @@ function coverSequenceExpression(path) {
|
|
|
311
265
|
const increment = newLineCoverageExpression(originFileId, originPos);
|
|
312
266
|
newExpressions.push(increment);
|
|
313
267
|
}
|
|
314
|
-
// We must add the expression to be evaluated after the coverage increment
|
|
315
|
-
// to not change the return value of the sequence expression.
|
|
316
268
|
newExpressions.push(expression);
|
|
317
269
|
}
|
|
318
270
|
path.replaceWith(T.sequenceExpression(newExpressions));
|
|
@@ -354,14 +306,11 @@ function convertArrowExpression(path) {
|
|
|
354
306
|
const T = this.types;
|
|
355
307
|
if (!T.isBlockStatement(node.body)) {
|
|
356
308
|
const bloc = node.body.loc;
|
|
357
|
-
if (node.expression
|
|
309
|
+
if (node.expression) {
|
|
358
310
|
node.expression = false;
|
|
359
311
|
}
|
|
360
312
|
node.body = T.blockStatement([T.returnStatement(node.body)]);
|
|
361
|
-
// restore body location
|
|
362
313
|
node.body.loc = bloc;
|
|
363
|
-
// set up the location for the return statement so it gets
|
|
364
|
-
// instrumented
|
|
365
314
|
node.body.body[0].loc = bloc;
|
|
366
315
|
}
|
|
367
316
|
}
|
|
@@ -383,8 +332,7 @@ function coverIfBranches(path) {
|
|
|
383
332
|
this.insertBranchCounter(path.get('alternate'), undefined);
|
|
384
333
|
}
|
|
385
334
|
}
|
|
386
|
-
function createSwitchBranch(
|
|
387
|
-
// Intentionally left blank
|
|
335
|
+
function createSwitchBranch() {
|
|
388
336
|
}
|
|
389
337
|
function coverSwitchCase(path) {
|
|
390
338
|
const T = this.types;
|
|
@@ -412,7 +360,7 @@ function coverTernary(path) {
|
|
|
412
360
|
function coverLogicalExpression(path) {
|
|
413
361
|
const T = this.types;
|
|
414
362
|
if (path.parentPath.node.type === 'LogicalExpression') {
|
|
415
|
-
return;
|
|
363
|
+
return;
|
|
416
364
|
}
|
|
417
365
|
const leaves = [];
|
|
418
366
|
this.findLeaves(path.node, leaves, undefined, undefined);
|
|
@@ -442,9 +390,9 @@ function coverLogicalExpression(path) {
|
|
|
442
390
|
const codeVisitor = {
|
|
443
391
|
ArrowFunctionExpression: entries(convertArrowExpression, coverFunction),
|
|
444
392
|
AssignmentPattern: entries(coverAssignmentPattern),
|
|
445
|
-
BlockStatement: entries(),
|
|
446
|
-
ExportDefaultDeclaration: entries(),
|
|
447
|
-
ExportNamedDeclaration: entries(),
|
|
393
|
+
BlockStatement: entries(),
|
|
394
|
+
ExportDefaultDeclaration: entries(),
|
|
395
|
+
ExportNamedDeclaration: entries(),
|
|
448
396
|
ClassMethod: entries(coverFunction),
|
|
449
397
|
ClassDeclaration: entries(parenthesizedExpressionProp('superClass')),
|
|
450
398
|
ClassProperty: entries(coverClassPropDeclarator),
|
|
@@ -457,7 +405,7 @@ const codeVisitor = {
|
|
|
457
405
|
ReturnStatement: entries(coverStatement),
|
|
458
406
|
ThrowStatement: entries(coverStatement),
|
|
459
407
|
TryStatement: entries(coverStatement),
|
|
460
|
-
VariableDeclaration: entries(),
|
|
408
|
+
VariableDeclaration: entries(),
|
|
461
409
|
VariableDeclarator: entries(coverVariableDeclarator),
|
|
462
410
|
IfStatement: entries(blockProp('consequent'), blockProp('alternate'), coverStatement, coverIfBranches),
|
|
463
411
|
ForStatement: entries(blockProp('body'), coverStatement),
|
|
@@ -475,18 +423,11 @@ const codeVisitor = {
|
|
|
475
423
|
LogicalExpression: entries(coverLogicalExpression),
|
|
476
424
|
SequenceExpression: entries(coverSequenceExpression),
|
|
477
425
|
};
|
|
478
|
-
/**
|
|
479
|
-
* The rewire plugin (and potentially other babel middleware)
|
|
480
|
-
* may cause files to be instrumented twice, see:
|
|
481
|
-
* https://github.com/istanbuljs/babel-plugin-istanbul/issues/94
|
|
482
|
-
* we should only instrument code for coverage the first time
|
|
483
|
-
* it's run through lib-instrument.
|
|
484
|
-
*/
|
|
485
426
|
function alreadyInstrumented(path, visitState) {
|
|
486
427
|
return path.scope.hasBinding(visitState.varName);
|
|
487
428
|
}
|
|
488
429
|
function getParentComments(path) {
|
|
489
|
-
if (!
|
|
430
|
+
if (!path?.parent) {
|
|
490
431
|
return [];
|
|
491
432
|
}
|
|
492
433
|
if (!('comments' in path.parent)) {
|
|
@@ -498,28 +439,8 @@ function shouldIgnoreFile(programNodePath) {
|
|
|
498
439
|
if (!programNodePath) {
|
|
499
440
|
return false;
|
|
500
441
|
}
|
|
501
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
502
442
|
return getParentComments(programNodePath).some(c => COMMENT_FILE_RE.test(c.value));
|
|
503
443
|
}
|
|
504
|
-
/**
|
|
505
|
-
* `programVisitor` is a `babel` adaptor for instrumentation.
|
|
506
|
-
*
|
|
507
|
-
* It returns an object with two methods `enter` and `exit`.
|
|
508
|
-
* These should be assigned to or called from `Program` entry and exit functions
|
|
509
|
-
* in a babel visitor.
|
|
510
|
-
*
|
|
511
|
-
* These functions do not make assumptions about the state set by Babel and thus
|
|
512
|
-
* can be used in a context other than a Babel plugin.
|
|
513
|
-
*
|
|
514
|
-
* The exit function returns an object that currently has the following keys:
|
|
515
|
-
*
|
|
516
|
-
* `fileCoverage` - the file coverage object created for the source file.
|
|
517
|
-
* `sourceMappingURL` - any source mapping URL found when processing the file.
|
|
518
|
-
*
|
|
519
|
-
* @param types - an instance of babel-types.
|
|
520
|
-
* @param inputSourceMapConsumer - access object for the source map of the input.
|
|
521
|
-
* @param opts - additional options.
|
|
522
|
-
*/
|
|
523
444
|
function programVisitor(types, inputSourceMapConsumer, opts) {
|
|
524
445
|
opts = { ...opts };
|
|
525
446
|
const visitState = new VisitState(types, inputSourceMapConsumer, opts.ignoreClassMethods, opts.reportLogic, opts.shouldInstrumentCallback);
|
|
@@ -548,12 +469,10 @@ function programVisitor(types, inputSourceMapConsumer, opts) {
|
|
|
548
469
|
body.unshift(...codeToPrependAst.program.body);
|
|
549
470
|
}
|
|
550
471
|
}
|
|
551
|
-
// Add a variable definition for each origin file on top of the file.
|
|
552
472
|
for (const [originPath, originId] of originData.originToIdMap.entries()) {
|
|
553
473
|
const declaration = newStringConstDeclarationNode(originId, originPath);
|
|
554
474
|
body.unshift(declaration);
|
|
555
475
|
}
|
|
556
|
-
// Add a token for signaling that the file has been instrumented.
|
|
557
476
|
if (opts.isInstrumentedToken) {
|
|
558
477
|
types.addComment(path.node, 'leading', opts.isInstrumentedToken, false);
|
|
559
478
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamscale/lib-instrument",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
4
4
|
"description": "Library for adding coverage statements to JS code; forked from istanbul-lib-coverage",
|
|
5
5
|
"author": "Krishnan Anantheswaran <kananthmail-github@yahoo.com>",
|
|
6
6
|
"maintainers": [
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"lib"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@babel/core": "^7.
|
|
18
|
-
"@babel/parser": "^7.
|
|
19
|
-
"@babel/traverse": "^7.
|
|
20
|
-
"@types/node": "^22.
|
|
21
|
-
"source-map": "^0.7.
|
|
22
|
-
"typescript": "^5.
|
|
17
|
+
"@babel/core": "^7.28.5",
|
|
18
|
+
"@babel/parser": "^7.28.5",
|
|
19
|
+
"@babel/traverse": "^7.28.5",
|
|
20
|
+
"@types/node": "^22.18.12",
|
|
21
|
+
"source-map": "^0.7.6",
|
|
22
|
+
"typescript": "^5.8.3"
|
|
23
23
|
},
|
|
24
24
|
"license": "BSD-3-Clause",
|
|
25
25
|
"homepage": "https://docs.teamscale.com/howto/setting-up-profiler-tga/javascript/",
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"clean": "rimraf lib tsconfig.tsbuildinfo",
|
|
31
31
|
"build": "tsc",
|
|
32
|
-
"test": "
|
|
32
|
+
"test": "node --import tsx --test test/**/*.test.ts"
|
|
33
33
|
}
|
|
34
34
|
}
|