supercov 3.0.5 → 4.0.1
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/docs/cli.md +2 -2
- package/docs/performance.md +8 -5
- package/docs/troubleshooting.md +17 -4
- package/docs/workspace-isolation.md +30 -6
- package/package.json +9 -9
- package/runtime/javascript/register.mjs +44 -4
- package/runtime/javascript/resolve-loader.mjs +38 -2
- package/runtime/javascript/runtime.mjs +48 -0
package/docs/cli.md
CHANGED
|
@@ -530,8 +530,8 @@ terminal or offline environment after the package has been downloaded.
|
|
|
530
530
|
| --- | --- |
|
|
531
531
|
| `SUPERCOV_SOURCE_ROOTS` | Comma-separated directories or files that hold your own code, in any language; everything else is left out |
|
|
532
532
|
| `SUPERCOV_TEST_KIND` | Label the wrapped command as a test level such as `unit` or `e2e` |
|
|
533
|
-
| `
|
|
534
|
-
| `
|
|
533
|
+
| `SUPERCOV_SOURCE_TOOLS` | Comma-separated names of tools the test command runs that read source as text, such as a linter or a spell checker, besides Biome, oxlint, dprint, cspell and knip. Each is run on your source as you wrote it instead of on the instrumented copy |
|
|
534
|
+
| `SUPERCOV_KEEP_WORKSPACE` | Set to `1` to leave the instrumented copy of the project in `.supercov/workspaces/` after the run, to inspect what the command ran |
|
|
535
535
|
|
|
536
536
|
Examples:
|
|
537
537
|
|
package/docs/performance.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Speed and storage
|
|
2
2
|
|
|
3
|
-
A Supercov run includes your test command
|
|
4
|
-
|
|
5
|
-
the
|
|
3
|
+
A Supercov run includes your test command and evidence publication, and for a
|
|
4
|
+
compiled language an instrumented build, which repeated passes can reuse when
|
|
5
|
+
the relevant inputs have not changed. A JavaScript project has no such build:
|
|
6
|
+
the command runs as given, and a build inside it is part of the command's time.
|
|
6
7
|
|
|
7
8
|
## See where the time went
|
|
8
9
|
|
|
@@ -17,7 +18,7 @@ The summary separates:
|
|
|
17
18
|
| Initialization | Recovery, project discovery, and input checks |
|
|
18
19
|
| Workspace preparation | Refreshing the isolated project copy |
|
|
19
20
|
| Adapter setup | Preparing the runner integration |
|
|
20
|
-
| Instrumented build | Building measured source, or almost nothing on a cache hit |
|
|
21
|
+
| Instrumented build | Building measured source for a compiled language, or almost nothing on a cache hit; 0 for JavaScript |
|
|
21
22
|
| Test command | The wrapped command, including browser, VM, or remote latency |
|
|
22
23
|
| Evidence publication | Validating and storing the completed run |
|
|
23
24
|
|
|
@@ -41,7 +42,9 @@ before Supercov starts and is not coverage-engine overhead.
|
|
|
41
42
|
|
|
42
43
|
Supercov reuses an instrumented build only when source, dependencies,
|
|
43
44
|
configuration, toolchain, build mode, and instrumenter identity match. A
|
|
44
|
-
possible mismatch triggers a fresh build rather than risking stale coverage.
|
|
45
|
+
possible mismatch triggers a fresh build rather than risking stale coverage. A
|
|
46
|
+
build inside a JavaScript project's command is the command's own and runs every
|
|
47
|
+
time, as it does without Supercov.
|
|
45
48
|
|
|
46
49
|
## Use focused runs carefully
|
|
47
50
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -142,6 +142,21 @@ Its lines, methods and simple branches stay measured; everything that needs a
|
|
|
142
142
|
probe is declared as a measurement limit for that file. Please report the file,
|
|
143
143
|
since Supercov aims to instrument every Ruby source correctly.
|
|
144
144
|
|
|
145
|
+
## The tests cannot find `dist/` or a production build
|
|
146
|
+
|
|
147
|
+
Supercov runs your command in a copy of the project, and the copy starts
|
|
148
|
+
without build output: `dist/`, `build/`, `.next/` and the like are not copied,
|
|
149
|
+
because they hold code that was never instrumented. Supercov does not run your
|
|
150
|
+
`build` script for you (earlier versions did). If the tests read build output, put
|
|
151
|
+
the build in the command:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npx supercov -- sh -c "npm run build && npm test"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
A `pretest` script, or a Playwright `webServer` that builds, does the same. The
|
|
158
|
+
build then compiles the instrumented copy, and what the tests run is measured.
|
|
159
|
+
|
|
145
160
|
## A test that reads your source fails under Supercov
|
|
146
161
|
|
|
147
162
|
A test that opens your source files and asserts on their text sees the probes,
|
|
@@ -220,10 +235,8 @@ runs remain intact.
|
|
|
220
235
|
|
|
221
236
|
## The first run is slow
|
|
222
237
|
|
|
223
|
-
The first run may include the npm download, browser or toolchain startup,
|
|
224
|
-
workspace creation
|
|
225
|
-
isolated build when source, dependencies, configuration, toolchain, and build
|
|
226
|
-
mode still match.
|
|
238
|
+
The first run may include the npm download, browser or toolchain startup, and
|
|
239
|
+
workspace creation.
|
|
227
240
|
|
|
228
241
|
Inspect the recorded phases with:
|
|
229
242
|
|
|
@@ -36,6 +36,19 @@ and the instrumented copy is not what you wrote:
|
|
|
36
36
|
the like) read each file as you wrote it, and don't see `.supercov`.
|
|
37
37
|
- The type check `next build` runs reads each file as you wrote it too, while
|
|
38
38
|
the build itself compiles the instrumented copy.
|
|
39
|
+
- Biome, oxlint, dprint, cspell and knip run on your source as you wrote it:
|
|
40
|
+
the copy's `node_modules/.bin` starts each of them in a view of the copy
|
|
41
|
+
that holds every file in its original text. A file the tool writes there
|
|
42
|
+
(a report, a cache) is the command's output like any other. To have another
|
|
43
|
+
tool that reads source as text run the same way, name it:
|
|
44
|
+
`SUPERCOV_SOURCE_TOOLS=typos,stylelint`.
|
|
45
|
+
- A change one of these tools makes to a source file (`prettier --write`,
|
|
46
|
+
`eslint --fix`, `biome check --write`, `dprint fmt`) is made in your
|
|
47
|
+
project when the command ends, and a tool later in the same command reads
|
|
48
|
+
the changed text. The tests ran the copy instrumented before the change, so
|
|
49
|
+
the run measured the file as it was, says so, and reads as stale; the next
|
|
50
|
+
run measures the changed file. A file you edited yourself while the command
|
|
51
|
+
ran keeps your edit, and the run names it.
|
|
39
52
|
- Coverage tools the command runs itself (tap, c8, nyc, Jest's and Vitest's
|
|
40
53
|
`--coverage`) still collect and report coverage. They measure the
|
|
41
54
|
instrumented copy, and report close to what they report without Supercov,
|
|
@@ -58,9 +71,19 @@ outputs.
|
|
|
58
71
|
The run prints where the synced files went, what stayed behind and what the
|
|
59
72
|
command deleted, by top-level directory (`test-results/ 6`, `.next/ 2547`).
|
|
60
73
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
The copy starts without build output: `dist/`, `build/`, `.next/` and the
|
|
75
|
+
like are not copied, because they hold code that was never instrumented.
|
|
76
|
+
Supercov runs the command you give it and no build of its own, so a suite that
|
|
77
|
+
needs build output has the build in its command:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx supercov -- sh -c "npm run build && npm test"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
A `pretest` script, or a Playwright `webServer` that builds, does the same.
|
|
84
|
+
When a run fails in a project that has a `build` script the command does not
|
|
85
|
+
reach, it says this. `SUPERCOV_KEEP_WORKSPACE=1` leaves the instrumented copy
|
|
86
|
+
in `.supercov/workspaces/` after a run, to inspect.
|
|
64
87
|
|
|
65
88
|
## Files Supercov creates
|
|
66
89
|
|
|
@@ -80,9 +103,10 @@ fallback instead of adopting or deleting it.
|
|
|
80
103
|
|
|
81
104
|
## Repeated runs and the build cache
|
|
82
105
|
|
|
83
|
-
|
|
84
|
-
match, Supercov can reuse the isolated instrumented build
|
|
85
|
-
not force an unrelated
|
|
106
|
+
For a compiled language, when source, dependencies, configuration, toolchain,
|
|
107
|
+
and build mode still match, Supercov can reuse the isolated instrumented build,
|
|
108
|
+
so test-only changes do not force an unrelated rebuild. A JavaScript project's
|
|
109
|
+
build is part of its command and runs every time.
|
|
86
110
|
|
|
87
111
|
Workspace refreshes are prepared separately and become active only when
|
|
88
112
|
complete. An interrupted refresh does not replace the last complete cache with
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supercov",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Coverage, security and code quality for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"coverage",
|
|
@@ -119,14 +119,14 @@
|
|
|
119
119
|
"test:windows-tls": "node scripts/windows-tls-test.mjs"
|
|
120
120
|
},
|
|
121
121
|
"optionalDependencies": {
|
|
122
|
-
"@supercov/cli-darwin-arm64": "
|
|
123
|
-
"@supercov/cli-darwin-x64": "
|
|
124
|
-
"@supercov/cli-linux-arm64-gnu": "
|
|
125
|
-
"@supercov/cli-linux-arm64-musl": "
|
|
126
|
-
"@supercov/cli-linux-x64-gnu": "
|
|
127
|
-
"@supercov/cli-linux-x64-musl": "
|
|
128
|
-
"@supercov/cli-win32-arm64": "
|
|
129
|
-
"@supercov/cli-win32-x64": "
|
|
122
|
+
"@supercov/cli-darwin-arm64": "4.0.1",
|
|
123
|
+
"@supercov/cli-darwin-x64": "4.0.1",
|
|
124
|
+
"@supercov/cli-linux-arm64-gnu": "4.0.1",
|
|
125
|
+
"@supercov/cli-linux-arm64-musl": "4.0.1",
|
|
126
|
+
"@supercov/cli-linux-x64-gnu": "4.0.1",
|
|
127
|
+
"@supercov/cli-linux-x64-musl": "4.0.1",
|
|
128
|
+
"@supercov/cli-win32-arm64": "4.0.1",
|
|
129
|
+
"@supercov/cli-win32-x64": "4.0.1"
|
|
130
130
|
},
|
|
131
131
|
"peerDependencies": {
|
|
132
132
|
"@playwright/test": ">=1.55.0",
|
|
@@ -8,7 +8,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
8
8
|
};
|
|
9
9
|
import Module, { register, syncBuiltinESMExports } from "node:module";
|
|
10
10
|
import fs, { closeSync, openSync, readFileSync, realpathSync, unlinkSync } from "node:fs";
|
|
11
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
11
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { installLaunchSupervisor, wrapImportedCapability } from "./launchSupervisor.mjs";
|
|
14
14
|
import { __supercovBindCapabilityWrapper } from "./capability.mjs";
|
|
@@ -205,6 +205,8 @@ function installAuthoredSourceView() {
|
|
|
205
205
|
authored = new Set();
|
|
206
206
|
}
|
|
207
207
|
const authoredRoot = fileURLToPath(new URL("./.authored/", import.meta.url));
|
|
208
|
+
// What a tool of the command wrote to a rewritten file: `--fix`, `--write`.
|
|
209
|
+
const changedRoot = fileURLToPath(new URL("./.changed/", import.meta.url));
|
|
208
210
|
const workspace = fileURLToPath(new URL("../../", import.meta.url));
|
|
209
211
|
const roots = [...new Set([workspace, (() => {
|
|
210
212
|
try {
|
|
@@ -231,9 +233,28 @@ function installAuthoredSourceView() {
|
|
|
231
233
|
}
|
|
232
234
|
return undefined;
|
|
233
235
|
};
|
|
236
|
+
const { existsSync: exists, mkdirSync: makeDirectory } = fs;
|
|
237
|
+
// A rewritten file reads as the command has it now: what one of its
|
|
238
|
+
// tools made of it, or what its author wrote.
|
|
234
239
|
const authoredPath = (path) => {
|
|
235
240
|
const local = inside(path);
|
|
236
|
-
|
|
241
|
+
if (local === undefined || !authored.has(local))
|
|
242
|
+
return path;
|
|
243
|
+
const changed = resolve(changedRoot, local);
|
|
244
|
+
return exists(changed) ? changed : resolve(authoredRoot, local);
|
|
245
|
+
};
|
|
246
|
+
// And it is written beside the instrumented copy, never over it. Prettier
|
|
247
|
+
// with `--write` and ESLint with `--fix` replaced the copy with the
|
|
248
|
+
// source they had read: the file then ran unmeasured, and one whose tests
|
|
249
|
+
// passed read 0% covered. Supercov gives the project the change when the
|
|
250
|
+
// command ends.
|
|
251
|
+
const changedPath = (path) => {
|
|
252
|
+
const local = inside(path);
|
|
253
|
+
if (local === undefined || !authored.has(local))
|
|
254
|
+
return path;
|
|
255
|
+
const changed = resolve(changedRoot, local);
|
|
256
|
+
makeDirectory(dirname(changed), { recursive: true });
|
|
257
|
+
return changed;
|
|
237
258
|
};
|
|
238
259
|
// A recursive listing names nested entries by relative path, or as
|
|
239
260
|
// entries whose parent is inside .supercov.
|
|
@@ -250,7 +271,17 @@ function installAuthoredSourceView() {
|
|
|
250
271
|
? entries
|
|
251
272
|
: entries.filter((entry) => !hidden(entry));
|
|
252
273
|
const { readFileSync: readSync, readFile: readCallback, readdirSync: listSync, readdir: listCallback } = fs;
|
|
253
|
-
const { readFile: readPromise, readdir: listPromise } = fs.promises;
|
|
274
|
+
const { readFile: readPromise, readdir: listPromise, writeFile: writePromise } = fs.promises;
|
|
275
|
+
const { writeFileSync: writeSync, writeFile: writeCallback } = fs;
|
|
276
|
+
fs.writeFileSync = function writeFileSync(path, ...rest) {
|
|
277
|
+
return Reflect.apply(writeSync, this, [changedPath(path), ...rest]);
|
|
278
|
+
};
|
|
279
|
+
fs.writeFile = function writeFile(path, ...rest) {
|
|
280
|
+
return Reflect.apply(writeCallback, this, [changedPath(path), ...rest]);
|
|
281
|
+
};
|
|
282
|
+
fs.promises.writeFile = function writeFile(path, ...rest) {
|
|
283
|
+
return Reflect.apply(writePromise, this, [changedPath(path), ...rest]);
|
|
284
|
+
};
|
|
254
285
|
fs.readFileSync = function readFileSync(path, ...rest) {
|
|
255
286
|
return Reflect.apply(readSync, this, [authoredPath(path), ...rest]);
|
|
256
287
|
};
|
|
@@ -390,7 +421,16 @@ function skipCoverageThresholds(tool) {
|
|
|
390
421
|
console.error(`[supercov] ${tool}'s coverage thresholds stay as configured`, error);
|
|
391
422
|
}
|
|
392
423
|
}
|
|
393
|
-
|
|
424
|
+
// A handler for `.ts` in the CommonJS loader that is not Node's own is a
|
|
425
|
+
// transpiler's require hook: ts-node/register, @swc/register and the like.
|
|
426
|
+
// It was there before this preload, and it is how the project's TypeScript
|
|
427
|
+
// loads. Any `--import` sends the entry point through the module loader
|
|
428
|
+
// instead, where such a file is read as an ES module and the hook never
|
|
429
|
+
// sees it; the loader is told so it can hand those files back.
|
|
430
|
+
const typescriptHandler = Module._extensions[".ts"];
|
|
431
|
+
register(new URL("./resolve-loader.mjs", import.meta.url), {
|
|
432
|
+
data: { typescriptRequireHook: typeof typescriptHandler === "function" && typescriptHandler.name !== "loadTS" },
|
|
433
|
+
});
|
|
394
434
|
if (process.env.SUPERCOV_DEBUG === "1") {
|
|
395
435
|
console.error("[supercov] preload", { entrypoint });
|
|
396
436
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve as resolvePath } from "node:path";
|
|
2
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
4
|
const GENERATED_TARGET = "__SUPERCOV_PLAYWRIGHT_MODULE__";
|
|
4
5
|
const TARGET = process.env.SUPERCOV_PLAYWRIGHT_MODULE ??
|
|
@@ -25,6 +26,41 @@ function belongsToProject(parentURL) {
|
|
|
25
26
|
const generatedURL = `${projectURL}.supercov/`;
|
|
26
27
|
return (parentURL.startsWith(projectURL) && !parentURL.startsWith(generatedURL));
|
|
27
28
|
}
|
|
29
|
+
let typescriptRequireHook = false;
|
|
30
|
+
export function initialize(data) {
|
|
31
|
+
typescriptRequireHook = data?.typescriptRequireHook === true;
|
|
32
|
+
}
|
|
33
|
+
const packageTypes = new Map();
|
|
34
|
+
function packageType(directory) {
|
|
35
|
+
if (packageTypes.has(directory))
|
|
36
|
+
return packageTypes.get(directory);
|
|
37
|
+
let type;
|
|
38
|
+
try {
|
|
39
|
+
type = JSON.parse(readFileSync(resolvePath(directory, "package.json"), "utf8")).type ?? "commonjs";
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
const parent = dirname(directory);
|
|
43
|
+
type = parent === directory ? "commonjs" : packageType(parent);
|
|
44
|
+
}
|
|
45
|
+
packageTypes.set(directory, type);
|
|
46
|
+
return type;
|
|
47
|
+
}
|
|
48
|
+
// `node -r ts-node/register --test tests/a.test.ts` in a package without
|
|
49
|
+
// `"type"` passed alone and failed here with ERR_MODULE_NOT_FOUND on an
|
|
50
|
+
// extensionless import: the preload made Node load the entry point as an ES
|
|
51
|
+
// module, past the require hook that compiles it. A TypeScript file of a
|
|
52
|
+
// CommonJS package is given back to the CommonJS loader, where that hook is.
|
|
53
|
+
function throughRequireHook(resolved) {
|
|
54
|
+
if (!typescriptRequireHook || !resolved?.url?.startsWith("file:") || resolved.url.includes("/node_modules/"))
|
|
55
|
+
return resolved;
|
|
56
|
+
if (!/\.(?:ts|tsx|cts)$/.test(resolved.url) || /\.d\.c?ts$/.test(resolved.url))
|
|
57
|
+
return resolved;
|
|
58
|
+
if (resolved.format === "module" || resolved.format === "module-typescript")
|
|
59
|
+
return resolved;
|
|
60
|
+
if (!resolved.url.endsWith(".cts") && packageType(dirname(fileURLToPath(resolved.url))) === "module")
|
|
61
|
+
return resolved;
|
|
62
|
+
return { ...resolved, format: "commonjs" };
|
|
63
|
+
}
|
|
28
64
|
export async function resolve(specifier, context, nextResolve) {
|
|
29
65
|
// Some transpilers preserve the source-relative runtime import while moving
|
|
30
66
|
// only the transformed application file to an output directory. The
|
|
@@ -73,7 +109,7 @@ export async function resolve(specifier, context, nextResolve) {
|
|
|
73
109
|
}
|
|
74
110
|
return nextResolve(REPLACEMENT, context);
|
|
75
111
|
}
|
|
76
|
-
return nextResolve(specifier, context);
|
|
112
|
+
return throughRequireHook(await nextResolve(specifier, context));
|
|
77
113
|
}
|
|
78
114
|
|
|
79
115
|
// Node's own test coverage filters files by `--test-coverage-include` and
|
|
@@ -1402,6 +1402,10 @@ function registerProbeV2(definition) {
|
|
|
1402
1402
|
// A selection's outcomes -- short falsy, short truthy, right falsy, right
|
|
1403
1403
|
// truthy -- are four consecutive points per site from the first index.
|
|
1404
1404
|
selectionPoints: definition.selectionPoints,
|
|
1405
|
+
// What selectEndV2 and selectNextV2 read, for each tree of more than two
|
|
1406
|
+
// leaves.
|
|
1407
|
+
selectionSteps: (definition.selectionTrees || []).map((tree) => tree[0]),
|
|
1408
|
+
selectionChoices: (definition.selectionTrees || []).map((tree) => tree[1]),
|
|
1405
1409
|
none: optionalCallEmptySpread
|
|
1406
1410
|
};
|
|
1407
1411
|
state.probeV2Files.add(file);
|
|
@@ -1626,6 +1630,44 @@ function selectPathV2(file, value) {
|
|
|
1626
1630
|
}
|
|
1627
1631
|
return value;
|
|
1628
1632
|
}
|
|
1633
|
+
// A selection tree whose leaves stay the program's own expressions: each is
|
|
1634
|
+
// the last operand of a comma that names it in the site's frame, so a type
|
|
1635
|
+
// checker reading the instrumented copy narrows through `a && a.b` as it does
|
|
1636
|
+
// in the source, and the tree's value is recorded here, once, for the leaf
|
|
1637
|
+
// that produced it. What a leaf before the last decided on the way is known
|
|
1638
|
+
// from where evaluation went next, and is recorded there as plain hits.
|
|
1639
|
+
// Two leaves: the left one is last only when it decided the result.
|
|
1640
|
+
function selectEnd2V2(file, first, value, right) {
|
|
1641
|
+
coverageHitV2(file, first + (right ? 2 : 0) + (value ? 1 : 0));
|
|
1642
|
+
return value;
|
|
1643
|
+
}
|
|
1644
|
+
// More: the leaf's steps, the ones selectPathV2 takes as arguments, come from
|
|
1645
|
+
// the file's table, whose entry for a tree is `[steps of each leaf, for each
|
|
1646
|
+
// leaf the points each leaf before it has decided by then]`.
|
|
1647
|
+
function selectEndV2(file, tree, value, leaf) {
|
|
1648
|
+
const steps = file.selectionSteps[tree][leaf];
|
|
1649
|
+
for (let index = 0; index < steps.length; index += 1) {
|
|
1650
|
+
const step = steps[index];
|
|
1651
|
+
const code = step & 3;
|
|
1652
|
+
const first = (step - code) / 4;
|
|
1653
|
+
if (code === 3)
|
|
1654
|
+
coverageHitV2(file, value ? first + 3 : first + 2);
|
|
1655
|
+
else if (code === 0 ? value : code === 1 ? !value : value !== null && value !== void 0)
|
|
1656
|
+
coverageHitV2(file, value ? first + 1 : first);
|
|
1657
|
+
else
|
|
1658
|
+
break;
|
|
1659
|
+
}
|
|
1660
|
+
return value;
|
|
1661
|
+
}
|
|
1662
|
+
// Where one of several leaves can come before a leaf, the frame says which.
|
|
1663
|
+
// Written out in the program as a choice, it would be a branch of the user's
|
|
1664
|
+
// line to a coverage tool the tests run.
|
|
1665
|
+
function selectNextV2(file, tree, from, to) {
|
|
1666
|
+
const points = file.selectionChoices[tree][to][from];
|
|
1667
|
+
if (points)
|
|
1668
|
+
for (let index = 0; index < points.length; index += 1)
|
|
1669
|
+
coverageHitV2(file, points[index]);
|
|
1670
|
+
}
|
|
1629
1671
|
// `x ||= y`, `x &&= y`, `x ??= y` keep their operator and their single
|
|
1630
1672
|
// evaluation of the target: the right side goes through selectRightV2 (or the
|
|
1631
1673
|
// named form, for an anonymous function the assignment would have named), and
|
|
@@ -1851,6 +1893,9 @@ const directRuntimeApi = {
|
|
|
1851
1893
|
selectNamedRightV2,
|
|
1852
1894
|
selectAssignEndV2,
|
|
1853
1895
|
selectPathV2,
|
|
1896
|
+
selectEnd2V2,
|
|
1897
|
+
selectEndV2,
|
|
1898
|
+
selectNextV2,
|
|
1854
1899
|
takeNodeAssertionPhases,
|
|
1855
1900
|
tryBegin,
|
|
1856
1901
|
tryCatch,
|
|
@@ -1913,6 +1958,9 @@ export {
|
|
|
1913
1958
|
selectNamedRightV2,
|
|
1914
1959
|
selectAssignEndV2,
|
|
1915
1960
|
selectPathV2,
|
|
1961
|
+
selectEnd2V2,
|
|
1962
|
+
selectEndV2,
|
|
1963
|
+
selectNextV2,
|
|
1916
1964
|
takeNodeAssertionPhases,
|
|
1917
1965
|
tryBegin,
|
|
1918
1966
|
tryCatch,
|