supercov 4.0.0 → 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.
@@ -39,10 +39,16 @@ and the instrumented copy is not what you wrote:
39
39
  - Biome, oxlint, dprint, cspell and knip run on your source as you wrote it:
40
40
  the copy's `node_modules/.bin` starts each of them in a view of the copy
41
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. A change it
43
- makes to a source file (`--write`, `--fix`) is not applied during a measured
44
- run, and the run says so. To have another tool that reads source as text run
45
- the same way, name it: `SUPERCOV_SOURCE_TOOLS=typos,stylelint`.
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.
46
52
  - Coverage tools the command runs itself (tap, c8, nyc, Jest's and Vitest's
47
53
  `--coverage`) still collect and report coverage. They measure the
48
54
  instrumented copy, and report close to what they report without Supercov,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "4.0.0",
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": "4.0.0",
123
- "@supercov/cli-darwin-x64": "4.0.0",
124
- "@supercov/cli-linux-arm64-gnu": "4.0.0",
125
- "@supercov/cli-linux-arm64-musl": "4.0.0",
126
- "@supercov/cli-linux-x64-gnu": "4.0.0",
127
- "@supercov/cli-linux-x64-musl": "4.0.0",
128
- "@supercov/cli-win32-arm64": "4.0.0",
129
- "@supercov/cli-win32-x64": "4.0.0"
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
- return local !== undefined && authored.has(local) ? resolve(authoredRoot, local) : path;
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
  };