supercov 0.0.11 → 0.0.13
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 +28 -23
- package/bin/supercov.js +8 -19
- package/package.json +31 -43
- package/{dist → runtime/javascript}/atomic.js +0 -1
- package/{dist → runtime/javascript}/launchSupervisor.js +45 -2
- package/{dist → runtime/javascript}/nodeAssert.js +0 -1
- package/{dist → runtime/javascript}/nodeAssertAdapter.js +0 -1
- package/{dist → runtime/javascript}/nodeAssertStrict.js +0 -1
- package/{dist → runtime/javascript}/nodeTest.js +0 -1
- package/{dist → runtime/javascript}/playwright.js +0 -1
- package/{dist → runtime/javascript}/playwrightReporter.js +0 -1
- package/{dist → runtime/javascript}/provenance.js +0 -1
- package/{dist → runtime/javascript}/register.mjs +3 -2
- package/{dist → runtime/javascript}/resolve-loader.mjs +0 -42
- package/{dist → runtime/javascript}/runnerEvidence.js +0 -1
- package/{dist → runtime/javascript}/transport.js +0 -1
- package/{dist → runtime/javascript}/vitest.js +0 -1
- package/{dist → runtime/javascript}/vitestReporter.js +0 -1
- package/dist/agentJson.d.ts +0 -45
- package/dist/agentJson.d.ts.map +0 -1
- package/dist/agentJson.js +0 -102
- package/dist/agentJson.js.map +0 -1
- package/dist/analyze.d.ts +0 -12
- package/dist/analyze.d.ts.map +0 -1
- package/dist/analyze.js +0 -704
- package/dist/analyze.js.map +0 -1
- package/dist/atomic.d.ts +0 -6
- package/dist/atomic.d.ts.map +0 -1
- package/dist/atomic.js.map +0 -1
- package/dist/buildCache.d.ts +0 -14
- package/dist/buildCache.d.ts.map +0 -1
- package/dist/buildCache.js +0 -84
- package/dist/buildCache.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -797
- package/dist/cli.js.map +0 -1
- package/dist/directInstrumenter.d.ts +0 -8
- package/dist/directInstrumenter.d.ts.map +0 -1
- package/dist/directInstrumenter.js +0 -112
- package/dist/directInstrumenter.js.map +0 -1
- package/dist/engineEvidence.d.ts +0 -9
- package/dist/engineEvidence.d.ts.map +0 -1
- package/dist/engineEvidence.js +0 -31
- package/dist/engineEvidence.js.map +0 -1
- package/dist/engineInstrumenter.d.ts +0 -16
- package/dist/engineInstrumenter.d.ts.map +0 -1
- package/dist/engineInstrumenter.js +0 -50
- package/dist/engineInstrumenter.js.map +0 -1
- package/dist/engineProcess.d.ts +0 -11
- package/dist/engineProcess.d.ts.map +0 -1
- package/dist/engineProcess.js +0 -59
- package/dist/engineProcess.js.map +0 -1
- package/dist/esmInterceptor.d.ts +0 -6
- package/dist/esmInterceptor.d.ts.map +0 -1
- package/dist/esmInterceptor.js +0 -63
- package/dist/esmInterceptor.js.map +0 -1
- package/dist/evidenceArchive.d.ts +0 -38
- package/dist/evidenceArchive.d.ts.map +0 -1
- package/dist/evidenceArchive.js +0 -156
- package/dist/evidenceArchive.js.map +0 -1
- package/dist/instrumenter.d.ts +0 -18
- package/dist/instrumenter.d.ts.map +0 -1
- package/dist/instrumenter.js +0 -1644
- package/dist/instrumenter.js.map +0 -1
- package/dist/integrity.d.ts +0 -10
- package/dist/integrity.d.ts.map +0 -1
- package/dist/integrity.js +0 -213
- package/dist/integrity.js.map +0 -1
- package/dist/launchSupervisor.d.ts +0 -38
- package/dist/launchSupervisor.d.ts.map +0 -1
- package/dist/launchSupervisor.js.map +0 -1
- package/dist/merge.d.ts +0 -2
- package/dist/merge.d.ts.map +0 -1
- package/dist/merge.js +0 -119
- package/dist/merge.js.map +0 -1
- package/dist/nodeAssert.d.ts +0 -31
- package/dist/nodeAssert.d.ts.map +0 -1
- package/dist/nodeAssert.js.map +0 -1
- package/dist/nodeAssertAdapter.d.ts +0 -6
- package/dist/nodeAssertAdapter.d.ts.map +0 -1
- package/dist/nodeAssertAdapter.js.map +0 -1
- package/dist/nodeAssertStrict.d.ts +0 -31
- package/dist/nodeAssertStrict.d.ts.map +0 -1
- package/dist/nodeAssertStrict.js.map +0 -1
- package/dist/nodeAssertionInstrumenter.d.ts +0 -8
- package/dist/nodeAssertionInstrumenter.d.ts.map +0 -1
- package/dist/nodeAssertionInstrumenter.js +0 -271
- package/dist/nodeAssertionInstrumenter.js.map +0 -1
- package/dist/nodeTest.d.ts +0 -15
- package/dist/nodeTest.d.ts.map +0 -1
- package/dist/nodeTest.js.map +0 -1
- package/dist/playwright.d.ts +0 -9
- package/dist/playwright.d.ts.map +0 -1
- package/dist/playwright.js.map +0 -1
- package/dist/playwrightReporter.d.ts +0 -6
- package/dist/playwrightReporter.d.ts.map +0 -1
- package/dist/playwrightReporter.js.map +0 -1
- package/dist/processDiagnostics.d.ts +0 -34
- package/dist/processDiagnostics.d.ts.map +0 -1
- package/dist/processDiagnostics.js +0 -155
- package/dist/processDiagnostics.js.map +0 -1
- package/dist/project.d.ts +0 -21
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -331
- package/dist/project.js.map +0 -1
- package/dist/provenance.d.ts +0 -9
- package/dist/provenance.d.ts.map +0 -1
- package/dist/provenance.js.map +0 -1
- package/dist/query.d.ts +0 -81
- package/dist/query.d.ts.map +0 -1
- package/dist/query.js +0 -1656
- package/dist/query.js.map +0 -1
- package/dist/queryCache.d.ts +0 -15
- package/dist/queryCache.d.ts.map +0 -1
- package/dist/queryCache.js +0 -95
- package/dist/queryCache.js.map +0 -1
- package/dist/queueAdapters.d.ts +0 -15
- package/dist/queueAdapters.d.ts.map +0 -1
- package/dist/queueAdapters.js +0 -55
- package/dist/queueAdapters.js.map +0 -1
- package/dist/register.d.mts +0 -2
- package/dist/register.d.mts.map +0 -1
- package/dist/register.mjs.map +0 -1
- package/dist/reporter.d.ts +0 -13
- package/dist/reporter.d.ts.map +0 -1
- package/dist/reporter.js +0 -252
- package/dist/reporter.js.map +0 -1
- package/dist/resolve-loader.d.mts +0 -3
- package/dist/resolve-loader.d.mts.map +0 -1
- package/dist/resolve-loader.mjs.map +0 -1
- package/dist/runAnalysis.d.ts +0 -19
- package/dist/runAnalysis.d.ts.map +0 -1
- package/dist/runAnalysis.js +0 -171
- package/dist/runAnalysis.js.map +0 -1
- package/dist/runnerEvidence.d.ts +0 -19
- package/dist/runnerEvidence.d.ts.map +0 -1
- package/dist/runnerEvidence.js.map +0 -1
- package/dist/runtime.d.ts +0 -140
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/runtimeIsolation.d.ts +0 -3
- package/dist/runtimeIsolation.d.ts.map +0 -1
- package/dist/runtimeIsolation.js +0 -9
- package/dist/runtimeIsolation.js.map +0 -1
- package/dist/sourceDiscovery.d.ts +0 -9
- package/dist/sourceDiscovery.d.ts.map +0 -1
- package/dist/sourceDiscovery.js +0 -242
- package/dist/sourceDiscovery.js.map +0 -1
- package/dist/transport.d.ts +0 -17
- package/dist/transport.d.ts.map +0 -1
- package/dist/transport.js.map +0 -1
- package/dist/types.d.ts +0 -378
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/vitePlugin.d.ts +0 -13
- package/dist/vitePlugin.d.ts.map +0 -1
- package/dist/vitePlugin.js +0 -139
- package/dist/vitePlugin.js.map +0 -1
- package/dist/vitest.d.ts +0 -2
- package/dist/vitest.d.ts.map +0 -1
- package/dist/vitest.js.map +0 -1
- package/dist/vitestReporter.d.ts +0 -43
- package/dist/vitestReporter.d.ts.map +0 -1
- package/dist/vitestReporter.js.map +0 -1
- package/dist/waivers.d.ts +0 -52
- package/dist/waivers.d.ts.map +0 -1
- package/dist/waivers.js +0 -121
- package/dist/waivers.js.map +0 -1
- package/dist/workspace.d.ts +0 -117
- package/dist/workspace.d.ts.map +0 -1
- package/dist/workspace.js +0 -680
- package/dist/workspace.js.map +0 -1
- /package/{dist → runtime/javascript}/runtime.js +0 -0
package/dist/cli.js
DELETED
|
@@ -1,797 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, } from "node:fs";
|
|
4
|
-
import { relative, resolve, sep } from "node:path";
|
|
5
|
-
import { performance } from "node:perf_hooks";
|
|
6
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
-
import { atomicRenameSync, atomicWriteFileSync } from "./atomic.js";
|
|
8
|
-
import { coverageQueryCommands, runQueryCommand } from "./query.js";
|
|
9
|
-
import { discoverCoverageProject, expandedCommand } from "./project.js";
|
|
10
|
-
import { createRunIntegrity } from "./integrity.js";
|
|
11
|
-
import { writeEngineEvidenceArchive } from "./engineEvidence.js";
|
|
12
|
-
import { mergeCoverageRuns } from "./merge.js";
|
|
13
|
-
import { buildCacheReusePaths, instrumentedBuildCacheKey, readInstrumentedBuildCache, writeInstrumentedBuildCache, } from "./buildCache.js";
|
|
14
|
-
import { instrumentDirectWorkspace } from "./directInstrumenter.js";
|
|
15
|
-
import { instrumentNodeAssertionsInWorkspace } from "./nodeAssertionInstrumenter.js";
|
|
16
|
-
import { acquireProjectLock, cachedWorkspacePath, cleanCoverageStorage, finalizePublishedRunStorage, prepareCachedWorkspace, pruneCachedWorkspaceSources, pruneCoverageStorage, recoverAbandonedRuns, removeStoredTreeDeferred, spawnTrashDeleter, updateRunState, writeRunState, } from "./workspace.js";
|
|
17
|
-
import { agentFailureJson, SupercovError } from "./agentJson.js";
|
|
18
|
-
import { isolateCollectorRuntime } from "./runtimeIsolation.js";
|
|
19
|
-
import { COMMAND_TERMINATION_GRACE_MS, COMMAND_TIMEOUT_EXIT_CODE, DEFAULT_DIAGNOSTIC_INTERVAL_MS, positiveMilliseconds, startProcessWatchdog, } from "./processDiagnostics.js";
|
|
20
|
-
function roundedTimings(timings) {
|
|
21
|
-
return Object.fromEntries(Object.entries(timings).map(([phase, duration]) => [
|
|
22
|
-
phase,
|
|
23
|
-
Math.round(duration * 10) / 10,
|
|
24
|
-
]));
|
|
25
|
-
}
|
|
26
|
-
function formatTimings(timings, totalMs) {
|
|
27
|
-
const rounded = roundedTimings(timings);
|
|
28
|
-
return [
|
|
29
|
-
`initialization=${rounded.initializationMs}ms`,
|
|
30
|
-
`workspace=${rounded.workspacePreparationMs}ms`,
|
|
31
|
-
`setup=${rounded.adapterSetupMs}ms`,
|
|
32
|
-
`build=${rounded.instrumentedBuildMs}ms`,
|
|
33
|
-
`tests=${rounded.testCommandMs}ms`,
|
|
34
|
-
`evidence=${rounded.evidencePublicationMs}ms`,
|
|
35
|
-
`total=${Math.round(totalMs * 10) / 10}ms`,
|
|
36
|
-
].join(" ");
|
|
37
|
-
}
|
|
38
|
-
let activeChild;
|
|
39
|
-
let signalEscalation;
|
|
40
|
-
function terminateChild(signal) {
|
|
41
|
-
const child = activeChild;
|
|
42
|
-
if (!child?.pid)
|
|
43
|
-
return;
|
|
44
|
-
try {
|
|
45
|
-
if (process.platform !== "win32")
|
|
46
|
-
process.kill(-child.pid, signal);
|
|
47
|
-
else
|
|
48
|
-
child.kill(signal);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
try {
|
|
52
|
-
child.kill(signal);
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
// The child may have exited between the signal and cleanup.
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function runChild(command, args, options) {
|
|
60
|
-
const diagnosticIntervalMs = positiveMilliseconds(options.env["SUPERCOV_DIAGNOSTIC_INTERVAL_MS"], "SUPERCOV_DIAGNOSTIC_INTERVAL_MS") ?? DEFAULT_DIAGNOSTIC_INTERVAL_MS;
|
|
61
|
-
const commandTimeoutMs = positiveMilliseconds(options.env["SUPERCOV_COMMAND_TIMEOUT_MS"], "SUPERCOV_COMMAND_TIMEOUT_MS");
|
|
62
|
-
return new Promise((resolveChild) => {
|
|
63
|
-
let error;
|
|
64
|
-
let timedOut = false;
|
|
65
|
-
let timeoutEscalation;
|
|
66
|
-
const child = spawn(command, args, {
|
|
67
|
-
cwd: options.cwd,
|
|
68
|
-
env: options.env,
|
|
69
|
-
stdio: "inherit",
|
|
70
|
-
detached: process.platform !== "win32",
|
|
71
|
-
});
|
|
72
|
-
activeChild = child;
|
|
73
|
-
const watchdog = startProcessWatchdog(child, {
|
|
74
|
-
diagnosticIntervalMs,
|
|
75
|
-
...(commandTimeoutMs === undefined ? {} : { timeoutMs: commandTimeoutMs }),
|
|
76
|
-
write(message) {
|
|
77
|
-
console.error(message);
|
|
78
|
-
},
|
|
79
|
-
onTimeout() {
|
|
80
|
-
if (timedOut)
|
|
81
|
-
return;
|
|
82
|
-
timedOut = true;
|
|
83
|
-
console.error(`[supercov] command exceeded SUPERCOV_COMMAND_TIMEOUT_MS=${commandTimeoutMs}; terminating process group`);
|
|
84
|
-
terminateChild("SIGTERM");
|
|
85
|
-
timeoutEscalation = setTimeout(() => terminateChild("SIGKILL"), COMMAND_TERMINATION_GRACE_MS);
|
|
86
|
-
timeoutEscalation.unref();
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
child.once("error", (failure) => {
|
|
90
|
-
error = failure;
|
|
91
|
-
});
|
|
92
|
-
child.once("close", (status, signal) => {
|
|
93
|
-
watchdog.stop();
|
|
94
|
-
if (timeoutEscalation)
|
|
95
|
-
clearTimeout(timeoutEscalation);
|
|
96
|
-
if (activeChild === child)
|
|
97
|
-
activeChild = undefined;
|
|
98
|
-
resolveChild({
|
|
99
|
-
status,
|
|
100
|
-
signal,
|
|
101
|
-
...(error ? { error } : {}),
|
|
102
|
-
...(timedOut ? { timedOut: true } : {}),
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
function exitCode(result) {
|
|
108
|
-
if (!result)
|
|
109
|
-
return 0;
|
|
110
|
-
if (result.timedOut)
|
|
111
|
-
return COMMAND_TIMEOUT_EXIT_CODE;
|
|
112
|
-
if (result.error)
|
|
113
|
-
return 1;
|
|
114
|
-
if (result.status !== null)
|
|
115
|
-
return result.status;
|
|
116
|
-
return result.signal ? 128 : 1;
|
|
117
|
-
}
|
|
118
|
-
function signalExitCode(signal) {
|
|
119
|
-
if (signal === "SIGHUP")
|
|
120
|
-
return 129;
|
|
121
|
-
if (signal === "SIGINT")
|
|
122
|
-
return 130;
|
|
123
|
-
if (signal === "SIGTERM")
|
|
124
|
-
return 143;
|
|
125
|
-
return 128;
|
|
126
|
-
}
|
|
127
|
-
function parseRetentionOptions(command, args) {
|
|
128
|
-
let keep = 20;
|
|
129
|
-
let dryRun = false;
|
|
130
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
131
|
-
const argument = args[index];
|
|
132
|
-
if (argument === "--dry-run")
|
|
133
|
-
dryRun = true;
|
|
134
|
-
else if (argument === "--keep") {
|
|
135
|
-
const value = Number(args[++index]);
|
|
136
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
137
|
-
throw new Error("--keep must be a non-negative integer");
|
|
138
|
-
keep = value;
|
|
139
|
-
}
|
|
140
|
-
else
|
|
141
|
-
throw new Error(`Unknown ${command} option: ${argument}`);
|
|
142
|
-
}
|
|
143
|
-
return { keep, dryRun };
|
|
144
|
-
}
|
|
145
|
-
function cleanCommand(args) {
|
|
146
|
-
const options = parseRetentionOptions("clean", args);
|
|
147
|
-
const result = cleanCoverageStorage(process.cwd(), options);
|
|
148
|
-
console.log(`[supercov] ${options.dryRun ? "would remove" : "removed"} ${result.removedRuns.length} stored run(s), ${result.removedWorkspaces.length} per-run workspace(s), and ${result.removedBuildCache ? "the" : "no"} isolated build cache; keeping ${options.keep} newest run(s)`);
|
|
149
|
-
for (const id of result.removedRuns)
|
|
150
|
-
console.log(id);
|
|
151
|
-
}
|
|
152
|
-
function pruneCommand(args) {
|
|
153
|
-
const options = parseRetentionOptions("prune", args);
|
|
154
|
-
const result = pruneCoverageStorage(process.cwd(), options);
|
|
155
|
-
console.log(`[supercov] ${options.dryRun ? "would remove" : "removed"} ${result.removedRuns.length} stored run(s), ${result.removedWorkspaces.length} terminal/orphan work director${result.removedWorkspaces.length === 1 ? "y" : "ies"}, and ${result.removedEvidence.length} loose evidence director${result.removedEvidence.length === 1 ? "y" : "ies"}; keeping ${options.keep} newest run(s) and preserving the shared cache`);
|
|
156
|
-
for (const id of result.removedRuns)
|
|
157
|
-
console.log(id);
|
|
158
|
-
}
|
|
159
|
-
async function createCoverageRun(command) {
|
|
160
|
-
const root = process.cwd();
|
|
161
|
-
const runId = new Date().toISOString().replace(/[:.]/g, "-");
|
|
162
|
-
const runStartedAt = Date.now();
|
|
163
|
-
const runStartedMonotonic = performance.now();
|
|
164
|
-
const timings = {
|
|
165
|
-
initializationMs: 0,
|
|
166
|
-
workspacePreparationMs: 0,
|
|
167
|
-
adapterSetupMs: 0,
|
|
168
|
-
instrumentedBuildMs: 0,
|
|
169
|
-
testCommandMs: 0,
|
|
170
|
-
evidencePublicationMs: 0,
|
|
171
|
-
};
|
|
172
|
-
const recovered = recoverAbandonedRuns(root);
|
|
173
|
-
if (recovered.length > 0)
|
|
174
|
-
console.error(`[supercov] recovered abandoned run(s): ${recovered.join(", ")}`);
|
|
175
|
-
const lock = acquireProjectLock(root, runId);
|
|
176
|
-
// The run store is derived local state; keep it out of the user's diff
|
|
177
|
-
// without asking them to edit their own .gitignore.
|
|
178
|
-
const storeIgnorePath = resolve(root, ".supercov/.gitignore");
|
|
179
|
-
if (!existsSync(storeIgnorePath))
|
|
180
|
-
atomicWriteFileSync(storeIgnorePath, "*\n");
|
|
181
|
-
const project = discoverCoverageProject(root, process.env, command);
|
|
182
|
-
const packageSource = fileURLToPath(new URL(".", import.meta.url));
|
|
183
|
-
const runIntegrity = createRunIntegrity(root, project, packageSource);
|
|
184
|
-
const workspace = cachedWorkspacePath(root);
|
|
185
|
-
const buildCacheKey = instrumentedBuildCacheKey(runIntegrity, project);
|
|
186
|
-
const reusableBuild = project.buildAdapter !== "direct"
|
|
187
|
-
? readInstrumentedBuildCache(workspace, buildCacheKey)
|
|
188
|
-
: undefined;
|
|
189
|
-
const serverEvidenceRoot = resolve(workspace, ".supercov/server-evidence");
|
|
190
|
-
const runStagingDirectory = resolve(root, ".supercov/work", runId, "run-publication");
|
|
191
|
-
const storedRunDirectory = resolve(root, ".supercov/runs", runId);
|
|
192
|
-
const startedAt = new Date(runStartedAt).toISOString();
|
|
193
|
-
writeRunState(root, runId, {
|
|
194
|
-
id: runId,
|
|
195
|
-
pid: process.pid,
|
|
196
|
-
root,
|
|
197
|
-
workspace,
|
|
198
|
-
startedAt,
|
|
199
|
-
status: "preparing",
|
|
200
|
-
});
|
|
201
|
-
let receivedSignal;
|
|
202
|
-
const signalHandlers = new Map();
|
|
203
|
-
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
204
|
-
const handler = () => {
|
|
205
|
-
if (receivedSignal)
|
|
206
|
-
return;
|
|
207
|
-
receivedSignal = signal;
|
|
208
|
-
try {
|
|
209
|
-
updateRunState(root, runId, { status: "interrupted", signal });
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
// State recovery on the next invocation remains the fallback.
|
|
213
|
-
}
|
|
214
|
-
terminateChild(signal);
|
|
215
|
-
signalEscalation = setTimeout(() => terminateChild("SIGKILL"), COMMAND_TERMINATION_GRACE_MS);
|
|
216
|
-
signalEscalation.unref();
|
|
217
|
-
};
|
|
218
|
-
signalHandlers.set(signal, handler);
|
|
219
|
-
process.once(signal, handler);
|
|
220
|
-
}
|
|
221
|
-
let buildResult;
|
|
222
|
-
let testResult;
|
|
223
|
-
let publicationFailed = false;
|
|
224
|
-
let runPublished = false;
|
|
225
|
-
let timingsPrinted = false;
|
|
226
|
-
timings.initializationMs = performance.now() - runStartedMonotonic;
|
|
227
|
-
try {
|
|
228
|
-
let phaseStarted = performance.now();
|
|
229
|
-
const isolatedRoot = prepareCachedWorkspace(root, {
|
|
230
|
-
...(reusableBuild
|
|
231
|
-
? { reusePaths: buildCacheReusePaths(reusableBuild) }
|
|
232
|
-
: {}),
|
|
233
|
-
});
|
|
234
|
-
timings.workspacePreparationMs = performance.now() - phaseStarted;
|
|
235
|
-
if (receivedSignal)
|
|
236
|
-
throw new Error(`Interrupted by ${receivedSignal}`);
|
|
237
|
-
phaseStarted = performance.now();
|
|
238
|
-
const generatedDirectory = resolve(isolatedRoot, ".supercov");
|
|
239
|
-
const evidenceDirectoryRelative = `.supercov/evidence/${runId}`;
|
|
240
|
-
const isolatedEvidenceDirectory = resolve(isolatedRoot, evidenceDirectoryRelative);
|
|
241
|
-
const persistedEvidenceDirectory = resolve(root, ".supercov/evidence", runId);
|
|
242
|
-
const generatedPlaywrightConfig = resolve(generatedDirectory, "playwright.config.mjs");
|
|
243
|
-
const generatedViteConfig = resolve(generatedDirectory, "vite.config.mjs");
|
|
244
|
-
const generatedVitestConfig = resolve(generatedDirectory, "vitest.config.mjs");
|
|
245
|
-
let installedJestMajor;
|
|
246
|
-
try {
|
|
247
|
-
const installed = JSON.parse(readFileSync(resolve(root, "node_modules/jest/package.json"), "utf8"));
|
|
248
|
-
installedJestMajor = Number.parseInt(installed.version?.split(".")[0] ?? "", 10);
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
// Yarn PnP and custom launchers may not expose a physical Jest package.
|
|
252
|
-
}
|
|
253
|
-
const usesLegacyJestConfig = installedJestMajor !== undefined && installedJestMajor < 28;
|
|
254
|
-
const generatedJestConfig = usesLegacyJestConfig
|
|
255
|
-
? resolve(generatedDirectory, "jest-cjs/config.js")
|
|
256
|
-
: resolve(generatedDirectory, "jest.config.mjs");
|
|
257
|
-
const manifestPath = resolve(generatedDirectory, "manifest.json");
|
|
258
|
-
const buildOutputMetadataPath = resolve(generatedDirectory, "build-outputs.json");
|
|
259
|
-
const isolatedPlaywrightConfig = project.playwrightConfig
|
|
260
|
-
? resolve(isolatedRoot, relative(root, project.playwrightConfig))
|
|
261
|
-
: undefined;
|
|
262
|
-
const isolatedVitestConfig = project.vitestConfig
|
|
263
|
-
? resolve(isolatedRoot, relative(root, project.vitestConfig))
|
|
264
|
-
: undefined;
|
|
265
|
-
const isolatedJestConfig = project.jestConfig
|
|
266
|
-
? resolve(isolatedRoot, relative(root, project.jestConfig))
|
|
267
|
-
: undefined;
|
|
268
|
-
mkdirSync(generatedDirectory, { recursive: true });
|
|
269
|
-
mkdirSync(isolatedEvidenceDirectory, { recursive: true });
|
|
270
|
-
atomicWriteFileSync(resolve(generatedDirectory, "package.json"), `${JSON.stringify({ private: true, type: "module" })}\n`);
|
|
271
|
-
if (usesLegacyJestConfig) {
|
|
272
|
-
mkdirSync(resolve(generatedDirectory, "jest-cjs"), { recursive: true });
|
|
273
|
-
atomicWriteFileSync(resolve(generatedDirectory, "jest-cjs/package.json"), `${JSON.stringify({ private: true, type: "commonjs" })}\n`);
|
|
274
|
-
}
|
|
275
|
-
for (const file of [
|
|
276
|
-
"atomic.js",
|
|
277
|
-
"launchSupervisor.js",
|
|
278
|
-
"nodeAssert.js",
|
|
279
|
-
"nodeAssertAdapter.js",
|
|
280
|
-
"nodeAssertStrict.js",
|
|
281
|
-
"nodeTest.js",
|
|
282
|
-
"playwright.js",
|
|
283
|
-
"playwrightReporter.js",
|
|
284
|
-
"provenance.js",
|
|
285
|
-
"register.mjs",
|
|
286
|
-
"resolve-loader.mjs",
|
|
287
|
-
"runtime.js",
|
|
288
|
-
"runnerEvidence.js",
|
|
289
|
-
"transport.js",
|
|
290
|
-
"types.js",
|
|
291
|
-
]) {
|
|
292
|
-
copyFileSync(resolve(packageSource, file), resolve(generatedDirectory, file));
|
|
293
|
-
}
|
|
294
|
-
const generatedRuntime = resolve(generatedDirectory, "runtime.js");
|
|
295
|
-
atomicWriteFileSync(generatedRuntime, isolateCollectorRuntime(readFileSync(generatedRuntime, "utf8"), `collector-${buildCacheKey}`));
|
|
296
|
-
atomicWriteFileSync(resolve(generatedDirectory, "runtime.d.ts"), `${[
|
|
297
|
-
"coverageHit",
|
|
298
|
-
"selectionBegin",
|
|
299
|
-
"selectionRight",
|
|
300
|
-
"selectionEnd",
|
|
301
|
-
"optionalSelect",
|
|
302
|
-
"optionalCallBegin",
|
|
303
|
-
"optionalCallReached",
|
|
304
|
-
"optionalCallContinued",
|
|
305
|
-
"optionalCallEnd",
|
|
306
|
-
"defaultSelected",
|
|
307
|
-
"defaultEntered",
|
|
308
|
-
"tryBegin",
|
|
309
|
-
"tryCatch",
|
|
310
|
-
"tryEnd",
|
|
311
|
-
"loopBegin",
|
|
312
|
-
"loopEntered",
|
|
313
|
-
"loopEnd",
|
|
314
|
-
"mcdcBegin",
|
|
315
|
-
"mcdcCondition",
|
|
316
|
-
"mcdcEnd",
|
|
317
|
-
"registerProbeV2",
|
|
318
|
-
"coverageHitV2",
|
|
319
|
-
"mcdcEndV2",
|
|
320
|
-
].map((name) => `export declare function ${name}(...args: any[]): any;`).join("\n")}\n`);
|
|
321
|
-
const generatedPlaywrightAdapter = resolve(generatedDirectory, "playwright.js");
|
|
322
|
-
const generatedPlaywrightReporter = resolve(generatedDirectory, "playwrightReporter.js");
|
|
323
|
-
atomicWriteFileSync(generatedPlaywrightAdapter, readFileSync(generatedPlaywrightAdapter, "utf8")
|
|
324
|
-
.replace("__SUPERCOV_EVIDENCE_DIRECTORY__", evidenceDirectoryRelative)
|
|
325
|
-
.replace("__SUPERCOV_PLAYWRIGHT_MODULE__", project.playwrightModule)
|
|
326
|
-
.replace("__SUPERCOV_PLAYWRIGHT_TEST_EXPORT__", project.playwrightTestExport)
|
|
327
|
-
.replace("/*__SUPERCOV_ADAPTER_EXPORTS__*/", [
|
|
328
|
-
...(project.playwrightTestExport === "test"
|
|
329
|
-
? []
|
|
330
|
-
: [
|
|
331
|
-
`export { instrumentedTest as ${project.playwrightTestExport} };`,
|
|
332
|
-
]),
|
|
333
|
-
...project.playwrightExports
|
|
334
|
-
.filter((name) => name !== "test" &&
|
|
335
|
-
name !== "expect" &&
|
|
336
|
-
name !== project.playwrightTestExport)
|
|
337
|
-
.map((name) => `export const ${name} = adapter[${JSON.stringify(name)}];`),
|
|
338
|
-
].join("\n"))
|
|
339
|
-
.replace("__SUPERCOV_RUN_ID__", runId));
|
|
340
|
-
atomicWriteFileSync(generatedPlaywrightReporter, readFileSync(generatedPlaywrightReporter, "utf8").replace("__SUPERCOV_EVIDENCE_DIRECTORY__", evidenceDirectoryRelative));
|
|
341
|
-
const generatedResolveLoader = resolve(generatedDirectory, "resolve-loader.mjs");
|
|
342
|
-
atomicWriteFileSync(generatedResolveLoader, readFileSync(generatedResolveLoader, "utf8").replace("__SUPERCOV_PLAYWRIGHT_MODULE__", project.playwrightModule));
|
|
343
|
-
// Strict (pnpm-style) installs never hoist vite to the project root, so
|
|
344
|
-
// the generated configs must import it exactly where the project's own
|
|
345
|
-
// tooling would find it: directly, or through vitest's dependencies.
|
|
346
|
-
const viteModuleSpecifier = (() => {
|
|
347
|
-
const projectRequire = createRequire(resolve(root, "package.json"));
|
|
348
|
-
try {
|
|
349
|
-
return pathToFileURL(projectRequire.resolve("vite")).href;
|
|
350
|
-
}
|
|
351
|
-
catch {
|
|
352
|
-
try {
|
|
353
|
-
return pathToFileURL(createRequire(projectRequire.resolve("vitest/package.json")).resolve("vite")).href;
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
return "vite";
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
})();
|
|
360
|
-
if (isolatedPlaywrightConfig) {
|
|
361
|
-
// Keep this import inside Playwright's own transform graph. In older
|
|
362
|
-
// supported releases, a native ESM file-URL import of a TypeScript
|
|
363
|
-
// config enters the synchronous transform bridge recursively and can
|
|
364
|
-
// deadlock in Atomics.wait before test discovery starts.
|
|
365
|
-
const configImport = `../${relative(isolatedRoot, isolatedPlaywrightConfig).split(sep).join("/")}`;
|
|
366
|
-
atomicWriteFileSync(generatedPlaywrightConfig, [
|
|
367
|
-
`import './register.mjs';`,
|
|
368
|
-
`import { dirname, isAbsolute, relative, resolve } from 'node:path';`,
|
|
369
|
-
`import { fileURLToPath } from 'node:url';`,
|
|
370
|
-
`import original from '${configImport}';`,
|
|
371
|
-
`const resolved = typeof original === 'function' ? await original({ command: 'test', mode: 'test' }) : original;`,
|
|
372
|
-
`const runtimeProjectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');`,
|
|
373
|
-
`const originalDirectory = dirname(fileURLToPath(new URL(${JSON.stringify(configImport)}, import.meta.url)));`,
|
|
374
|
-
`const sourceProjectRoot = process.env.SUPERCOV_SOURCE_PROJECT_ROOT;`,
|
|
375
|
-
`const runtimePath = value => {`,
|
|
376
|
-
` if (!value) return value;`,
|
|
377
|
-
` const absolute = isAbsolute(value) ? value : resolve(originalDirectory, value);`,
|
|
378
|
-
` const local = relative(runtimeProjectRoot, absolute);`,
|
|
379
|
-
` if (local === '' || (!local.startsWith('..') && !isAbsolute(local))) return absolute;`,
|
|
380
|
-
` if (sourceProjectRoot) {`,
|
|
381
|
-
` const sourceLocal = relative(sourceProjectRoot, absolute);`,
|
|
382
|
-
` if (sourceLocal === '' || (!sourceLocal.startsWith('..') && !isAbsolute(sourceLocal))) return resolve(runtimeProjectRoot, sourceLocal);`,
|
|
383
|
-
` }`,
|
|
384
|
-
` throw new Error('Supercov refuses a Playwright output/cwd outside the isolated project: ' + absolute);`,
|
|
385
|
-
`};`,
|
|
386
|
-
`const normalizeWebServer = server => server ? ({ ...server, cwd: runtimePath(server.cwd ?? originalDirectory) }) : server;`,
|
|
387
|
-
`const normalized = { ...resolved,`,
|
|
388
|
-
` testDir: runtimePath(resolved?.testDir),`,
|
|
389
|
-
` outputDir: runtimePath(resolved?.outputDir),`,
|
|
390
|
-
` snapshotDir: runtimePath(resolved?.snapshotDir),`,
|
|
391
|
-
` projects: resolved?.projects?.map(project => ({ ...project, testDir: runtimePath(project.testDir), outputDir: runtimePath(project.outputDir), snapshotDir: runtimePath(project.snapshotDir) })),`,
|
|
392
|
-
` webServer: Array.isArray(resolved?.webServer) ? resolved.webServer.map(normalizeWebServer) : normalizeWebServer(resolved?.webServer),`,
|
|
393
|
-
`};`,
|
|
394
|
-
`const configuredReporters = normalized.reporter;`,
|
|
395
|
-
`const reporters = configuredReporters`,
|
|
396
|
-
` ? (typeof configuredReporters === 'string' ? [[configuredReporters]] : (Array.isArray(configuredReporters[0]) ? configuredReporters : [configuredReporters]))`,
|
|
397
|
-
` : [['list']];`,
|
|
398
|
-
`const coverageReporter = resolve(runtimeProjectRoot, '.supercov/playwrightReporter.js');`,
|
|
399
|
-
`export default { ...normalized, reporter: [...reporters, [coverageReporter]] };`,
|
|
400
|
-
"",
|
|
401
|
-
].join("\n"));
|
|
402
|
-
}
|
|
403
|
-
atomicWriteFileSync(generatedViteConfig, [
|
|
404
|
-
`import * as viteNamespace from '${viteModuleSpecifier}';`,
|
|
405
|
-
`import { isAbsolute, relative, resolve } from 'node:path';`,
|
|
406
|
-
`import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.js")).href}';`,
|
|
407
|
-
`const vite = viteNamespace.default ?? viteNamespace;`,
|
|
408
|
-
`const { loadConfigFromFile, mergeConfig } = vite;`,
|
|
409
|
-
`export default async function supercovViteConfig(env) {`,
|
|
410
|
-
` const loaded = await loadConfigFromFile(env, undefined, process.cwd());`,
|
|
411
|
-
` const originalRoot = ${JSON.stringify(root)};`,
|
|
412
|
-
` const isolatedRoot = ${JSON.stringify(isolatedRoot)};`,
|
|
413
|
-
` const relocate = (value, label) => {`,
|
|
414
|
-
` const absolute = isAbsolute(value) ? value : resolve(isolatedRoot, value);`,
|
|
415
|
-
` const alreadyIsolated = relative(isolatedRoot, absolute);`,
|
|
416
|
-
` if (alreadyIsolated === '' || (!alreadyIsolated.startsWith('..') && !isAbsolute(alreadyIsolated))) return absolute;`,
|
|
417
|
-
` const local = relative(originalRoot, absolute);`,
|
|
418
|
-
` if (local.startsWith('..') || isAbsolute(local)) throw new Error('Supercov refuses ' + label + ' outside the isolated project: ' + absolute);`,
|
|
419
|
-
` return resolve(isolatedRoot, local);`,
|
|
420
|
-
` };`,
|
|
421
|
-
` const config = loaded?.config ?? {};`,
|
|
422
|
-
` const relocateOutput = output => output ? ({ ...output, dir: output.dir ? relocate(output.dir, 'Rollup output') : output.dir, file: output.file ? relocate(output.file, 'Rollup output') : output.file }) : output;`,
|
|
423
|
-
` const rollupOutput = config.build?.rollupOptions?.output;`,
|
|
424
|
-
` const safe = { ...config, cacheDir: resolve(isolatedRoot, '.supercov/vite-cache'), build: { ...config.build, outDir: relocate(config.build?.outDir ?? 'dist', 'Vite build output'), rollupOptions: { ...config.build?.rollupOptions, output: Array.isArray(rollupOutput) ? rollupOutput.map(relocateOutput) : relocateOutput(rollupOutput) } } };`,
|
|
425
|
-
` return mergeConfig(safe, { plugins: [mcdcVitePlugin(${JSON.stringify({ root: isolatedRoot, sourceRoots: project.sourceRoots, sourceFiles: project.sourceFiles, sourceScope: project.sourceScope, sourceLimitations: project.sourceLimitations, manifestPath, buildOutputMetadataPath })})] });`,
|
|
426
|
-
`}`,
|
|
427
|
-
"",
|
|
428
|
-
].join("\n"));
|
|
429
|
-
atomicWriteFileSync(generatedVitestConfig, [
|
|
430
|
-
`import * as viteNamespace from '${viteModuleSpecifier}';`,
|
|
431
|
-
`import { resolve } from 'node:path';`,
|
|
432
|
-
`import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.js")).href}';`,
|
|
433
|
-
`import SupercovVitestReporter from '${pathToFileURL(resolve(packageSource, "vitestReporter.js")).href}';`,
|
|
434
|
-
`const vite = viteNamespace.default ?? viteNamespace;`,
|
|
435
|
-
`const { loadConfigFromFile, mergeConfig } = vite;`,
|
|
436
|
-
`const discoveredConfig = ${JSON.stringify(isolatedVitestConfig)};`,
|
|
437
|
-
`export default async function supercovVitestConfig(env) {`,
|
|
438
|
-
` const originalPath = process.env.SUPERCOV_ORIGINAL_VITEST_CONFIG || discoveredConfig;`,
|
|
439
|
-
` const loaded = originalPath ? await loadConfigFromFile(env, originalPath, process.cwd()) : undefined;`,
|
|
440
|
-
` const config = mergeConfig(loaded?.config ?? {}, {`,
|
|
441
|
-
` cacheDir: resolve(process.cwd(), '.supercov/vitest-cache'),`,
|
|
442
|
-
` plugins: ${project.buildAdapter === "vite" ? `[mcdcVitePlugin(${JSON.stringify({ root: isolatedRoot, sourceRoots: project.sourceRoots, sourceFiles: project.sourceFiles, sourceScope: project.sourceScope, sourceLimitations: project.sourceLimitations, manifestPath })})]` : "[]"},`,
|
|
443
|
-
` test: { setupFiles: [${JSON.stringify(resolve(packageSource, "vitest.js"))}], maxConcurrency: 1 },`,
|
|
444
|
-
` });`,
|
|
445
|
-
` const configuredReporters = loaded?.config?.test?.reporters;`,
|
|
446
|
-
` config.test ??= {};`,
|
|
447
|
-
` config.test.reporters = configuredReporters`,
|
|
448
|
-
` ? [...(Array.isArray(configuredReporters) ? configuredReporters : [configuredReporters]), new SupercovVitestReporter()]`,
|
|
449
|
-
` : ['default', new SupercovVitestReporter()];`,
|
|
450
|
-
` return config;`,
|
|
451
|
-
`}`,
|
|
452
|
-
"",
|
|
453
|
-
].join("\n"));
|
|
454
|
-
const generatedJestSetup = resolve(generatedDirectory, "jest.setup.cjs");
|
|
455
|
-
const generatedJestReporter = resolve(generatedDirectory, "jest-reporter.cjs");
|
|
456
|
-
const jestEvidenceHelpers = [
|
|
457
|
-
`const { createHash } = require('crypto');`,
|
|
458
|
-
`const { mkdirSync, renameSync, writeFileSync } = require('fs');`,
|
|
459
|
-
`const { relative, resolve, sep } = require('path');`,
|
|
460
|
-
`const local = file => file ? relative(process.cwd(), file).split(sep).join('/') : 'unknown';`,
|
|
461
|
-
`const id = (file, name) => 'jest:' + createHash('sha256').update(['jest', local(file), 0, 0, name].join('\\0')).digest('hex').slice(0, 24);`,
|
|
462
|
-
`const scope = (file, name, retry) => { const testId = id(file, name); const testKey = createHash('sha256').update(testId).digest('hex').slice(0, 24); return { version: 1, runId: process.env.SUPERCOV_RUN_ID || 'unscoped', workerId: 'jest-' + (process.env.JEST_WORKER_ID || process.pid), testId, testKey, retry, attemptId: testKey + '-' + retry }; };`,
|
|
463
|
-
`const provenance = file => ({ runner: 'jest', kind: process.env.SUPERCOV_TEST_KIND || (/(^|[/_.-])(e2e|end-to-end|offline|online)([/_.-]|$)/i.test(file) ? 'e2e' : /(^|[/_.-])(integration|int)([/_.-]|$)/i.test(file) ? 'integration' : /(^|[/_.-])(component|components|ct)([/_.-]|$)/i.test(file) ? 'component' : 'unit'), source: process.env.SUPERCOV_TEST_KIND ? 'explicit' : 'runner-default' });`,
|
|
464
|
-
`const write = (payload, suffix) => { const base = process.env.SUPERCOV_EVIDENCE_DIR; if (!base) return; const directory = resolve(process.cwd(), base, suffix); mkdirSync(directory, { recursive: true }); const target = resolve(directory, 'mcdc.json'); const temporary = target + '.' + process.pid + '.' + Math.random().toString(16).slice(2) + '.tmp'; writeFileSync(temporary, JSON.stringify(payload) + '\\n'); renameSync(temporary, target); };`,
|
|
465
|
-
];
|
|
466
|
-
atomicWriteFileSync(generatedJestSetup, [
|
|
467
|
-
...jestEvidenceHelpers,
|
|
468
|
-
`const { bind: bindJestEach } = require('jest-each');`,
|
|
469
|
-
`const attempts = new Map();`,
|
|
470
|
-
`const suites = [];`,
|
|
471
|
-
`const copyModifiers = (target, source, wrap, ancestors = new Set()) => { if (ancestors.has(source)) return target; const seen = new Set(ancestors); seen.add(source); for (const key of Object.getOwnPropertyNames(source)) { if (['length', 'name', 'prototype'].includes(key)) continue; const value = source[key]; if (typeof value !== 'function') continue; try { const decorated = key === 'each' ? (...eachArgs) => wrap(value.apply(source, eachArgs), eachArgs) : copyModifiers(wrap(value.bind(source)), value, wrap, seen); Object.defineProperty(target, key, { configurable: true, enumerable: true, value: decorated }); } catch {} } return target; };`,
|
|
472
|
-
`const wrapTest = (original, eachArgs) => { const wrapped = function(...args) { const callbackIndex = args.findLastIndex(value => typeof value === 'function'); if (callbackIndex < 0) return original.apply(this, args); const callback = args[callbackIndex]; const title = typeof args[0] === 'string' ? args[0] : callback.name || 'anonymous test'; const expandedNames = []; if (eachArgs) { try { bindJestEach(name => expandedNames.push([...suites, name].join(' ')))(...eachArgs)(title, callback, args[callbackIndex + 1]); } catch {} } const registeredName = [...suites, title].join(' '); let invocation = 0; const next = [...args]; const execute = (owner, originalCallbackArgs) => { const state = expect.getState(); const file = state.testPath; const name = expandedNames.length ? expandedNames[invocation++ % expandedNames.length] : registeredName; const testId = id(file, name); const retry = attempts.get(testId) || 0; attempts.set(testId, retry + 1); const execution = scope(file, name, retry); const runtime = process.__SUPERCOV_DIRECT_RUNTIME__; runtime.beginBufferedServerEvidence(execution); let flushed = false; const flush = () => { if (!flushed) { flushed = true; runtime.flushBufferedServerEvidence(execution); } }; const callbackArgs = [...originalCallbackArgs]; const doneIndex = callbackArgs.findLastIndex(value => typeof value === 'function'); if (doneIndex >= 0) { const done = callbackArgs[doneIndex]; callbackArgs[doneIndex] = (...doneArgs) => { flush(); return done(...doneArgs); }; } try { const result = runtime.withCoverageCarrier({ version: 1, scope: execution }, () => callback.apply(owner, callbackArgs)); if (result && typeof result.then === 'function') return Promise.resolve(result).finally(flush); if (doneIndex < 0) flush(); return result; } catch (error) { flush(); throw error; } }; let instrumented; if (eachArgs) { instrumented = function(...callbackArgs) { return execute(this, callbackArgs); }; try { Object.defineProperty(instrumented, 'length', { value: callback.length }); } catch {} } else { instrumented = callback.length ? function(done) { return execute(this, [done]); } : function() { return execute(this, []); }; } next[callbackIndex] = instrumented; return original.apply(this, next); }; return copyModifiers(wrapped, original, wrapTest); };`,
|
|
473
|
-
`const wrapSuite = original => { const wrapped = function(...args) { const callbackIndex = args.findLastIndex(value => typeof value === 'function'); if (callbackIndex < 0) return original.apply(this, args); const callback = args[callbackIndex]; const name = typeof args[0] === 'string' ? args[0] : callback.name || 'suite'; const next = [...args]; next[callbackIndex] = function(...callbackArgs) { suites.push(name); try { return callback.apply(this, callbackArgs); } finally { suites.pop(); } }; return original.apply(this, next); }; return copyModifiers(wrapped, original, wrapSuite); };`,
|
|
474
|
-
`globalThis.test = wrapTest(globalThis.test);`,
|
|
475
|
-
`globalThis.it = globalThis.test;`,
|
|
476
|
-
`globalThis.describe = wrapSuite(globalThis.describe);`,
|
|
477
|
-
"",
|
|
478
|
-
].join("\n"));
|
|
479
|
-
atomicWriteFileSync(generatedJestReporter, [
|
|
480
|
-
...jestEvidenceHelpers,
|
|
481
|
-
`module.exports = class SupercovJestReporter {`,
|
|
482
|
-
` onTestResult(_suite, result) { for (const assertion of result.testResults || []) {`,
|
|
483
|
-
` const file = result.testFilePath; const name = assertion.fullName || [...(assertion.ancestorTitles || []), assertion.title || 'unknown test'].join(' '); const retry = Math.max(0, (assertion.invocations || 1) - 1); const execution = scope(file, name, retry); const status = assertion.status === 'passed' ? 'passed' : assertion.status === 'failed' ? 'failed' : ['pending', 'todo', 'disabled'].includes(assertion.status) ? 'skipped' : 'unknown';`,
|
|
484
|
-
` write({ testId: execution.testId, scope: execution, test: name, testFile: local(file), title: assertion.title || name, retry, status, flaky: (assertion.retryReasons || []).length > 0, provenance: provenance(local(file)), runtime: [], browser: [], server: [] }, 'jest-' + execution.attemptId + '-status');`,
|
|
485
|
-
` } }`,
|
|
486
|
-
`};`,
|
|
487
|
-
"",
|
|
488
|
-
].join("\n"));
|
|
489
|
-
// Jest configuration commonly lives in the package.json "jest" field;
|
|
490
|
-
// replacing it with {} silently changes test discovery (roots, testRegex).
|
|
491
|
-
const packageJestConfig = (() => {
|
|
492
|
-
try {
|
|
493
|
-
return JSON.parse(readFileSync(resolve(root, "package.json"), "utf8")).jest;
|
|
494
|
-
}
|
|
495
|
-
catch {
|
|
496
|
-
return undefined;
|
|
497
|
-
}
|
|
498
|
-
})();
|
|
499
|
-
const jestOriginal = isolatedJestConfig
|
|
500
|
-
? usesLegacyJestConfig
|
|
501
|
-
? `const originalModule = require(${JSON.stringify(isolatedJestConfig)});\nconst original = typeof originalModule === 'function' ? originalModule() : originalModule;`
|
|
502
|
-
: `import originalModule from ${JSON.stringify(pathToFileURL(isolatedJestConfig).href)};\nconst original = typeof originalModule === 'function' ? await originalModule() : originalModule;`
|
|
503
|
-
: `const original = ${JSON.stringify(packageJestConfig ?? {})};`;
|
|
504
|
-
atomicWriteFileSync(generatedJestConfig, [
|
|
505
|
-
usesLegacyJestConfig
|
|
506
|
-
? `const { isAbsolute, resolve } = require('node:path');`
|
|
507
|
-
: `import { isAbsolute, resolve } from 'node:path';`,
|
|
508
|
-
jestOriginal,
|
|
509
|
-
`const decorate = config => ({ ...config,`,
|
|
510
|
-
` rootDir: config?.rootDir ? (isAbsolute(config.rootDir) ? config.rootDir : resolve(${JSON.stringify(isolatedJestConfig ? resolve(isolatedJestConfig, "..") : isolatedRoot)}, config.rootDir)) : ${JSON.stringify(isolatedRoot)},`,
|
|
511
|
-
// Jest matches ignore patterns against absolute test paths, and the
|
|
512
|
-
// isolated workspace path itself contains a node_modules segment.
|
|
513
|
-
// Anchor the default pattern below the workspace so it keeps ignoring
|
|
514
|
-
// the project's dependencies without ignoring the whole workspace.
|
|
515
|
-
` testPathIgnorePatterns: (config?.testPathIgnorePatterns ?? ['/node_modules/']).map(pattern => pattern === '/node_modules/' ? '<rootDir>/.*node_modules/' : pattern),`,
|
|
516
|
-
// Supercov replaces coverage measurement inside the wrapped run; the
|
|
517
|
-
// project's own istanbul pass would measure instrumented code and
|
|
518
|
-
// fail any thresholds against meaningless numbers.
|
|
519
|
-
` collectCoverage: false,`,
|
|
520
|
-
` coverageThreshold: undefined,`,
|
|
521
|
-
` setupFilesAfterEnv: [...(config?.setupFilesAfterEnv ?? []), ${JSON.stringify(generatedJestSetup)}],`,
|
|
522
|
-
` reporters: [...(config?.reporters ?? ['default']), ${JSON.stringify(generatedJestReporter)}],`,
|
|
523
|
-
` testLocationInResults: true,`,
|
|
524
|
-
`});`,
|
|
525
|
-
usesLegacyJestConfig
|
|
526
|
-
? `module.exports = { ...decorate(original), projects: original && original.projects && original.projects.map(project => typeof project === 'object' ? decorate(project) : project) };`
|
|
527
|
-
: `export default { ...decorate(original), projects: original?.projects?.map(project => typeof project === 'object' ? decorate(project) : project) };`,
|
|
528
|
-
"",
|
|
529
|
-
].join("\n"));
|
|
530
|
-
const coverageEnv = {
|
|
531
|
-
...process.env,
|
|
532
|
-
SUPERCOV_EVIDENCE_DIR: evidenceDirectoryRelative,
|
|
533
|
-
SUPERCOV_DIAGNOSTIC_OWNER_FILE: resolve(generatedDirectory, `diagnostic-owner-${runId}`),
|
|
534
|
-
SUPERCOV_EXECUTION_FINGERPRINT: runIntegrity.fingerprint.execution,
|
|
535
|
-
SUPERCOV_EXECUTION_LOG: resolve(isolatedEvidenceDirectory, "execution.jsonl"),
|
|
536
|
-
SUPERCOV_ESM_TRANSFORMER: pathToFileURL(resolve(packageSource, "esmInterceptor.js")).href,
|
|
537
|
-
SUPERCOV_ESM_CAPABILITY_WRAPPER: pathToFileURL(resolve(generatedDirectory, "launchSupervisor.js")).href,
|
|
538
|
-
SUPERCOV_RUN_ID: runId,
|
|
539
|
-
SUPERCOV_SERVER_EVIDENCE_ROOT: serverEvidenceRoot,
|
|
540
|
-
SUPERCOV_MANIFEST: manifestPath,
|
|
541
|
-
SUPERCOV_PLAYWRIGHT_MODULE: project.playwrightModule,
|
|
542
|
-
SUPERCOV_PLAYWRIGHT_TEST_EXPORT: project.playwrightTestExport,
|
|
543
|
-
SUPERCOV_PROJECT_ROOT: isolatedRoot,
|
|
544
|
-
SUPERCOV_SOURCE_PROJECT_ROOT: root,
|
|
545
|
-
...(project.buildAdapter === "direct" || project.usesJest
|
|
546
|
-
? { SUPERCOV_DIRECT_INSTRUMENTATION: "1" }
|
|
547
|
-
: {}),
|
|
548
|
-
SUPERCOV_GENERATED_VITEST_CONFIG: generatedVitestConfig,
|
|
549
|
-
SUPERCOV_GENERATED_PLAYWRIGHT_CONFIG: generatedPlaywrightConfig,
|
|
550
|
-
SUPERCOV_GENERATED_JEST_CONFIG: generatedJestConfig,
|
|
551
|
-
...(isolatedPlaywrightConfig
|
|
552
|
-
? { SUPERCOV_ORIGINAL_PLAYWRIGHT_CONFIG: isolatedPlaywrightConfig }
|
|
553
|
-
: {}),
|
|
554
|
-
};
|
|
555
|
-
const testNodeOptions = [
|
|
556
|
-
process.env["NODE_OPTIONS"],
|
|
557
|
-
`--import=${pathToFileURL(resolve(generatedDirectory, "register.mjs")).href}`,
|
|
558
|
-
]
|
|
559
|
-
.filter(Boolean)
|
|
560
|
-
.join(" ");
|
|
561
|
-
timings.adapterSetupMs = performance.now() - phaseStarted;
|
|
562
|
-
updateRunState(root, runId, { status: "building" });
|
|
563
|
-
console.error(`[supercov] instrumenting isolated workspace ${isolatedRoot}`);
|
|
564
|
-
if (Object.keys(project.buildEnvironment).length > 0)
|
|
565
|
-
console.error(`[supercov] inferred build mode from command/config: ${Object.entries(project.buildEnvironment)
|
|
566
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
567
|
-
.join(" ")}`);
|
|
568
|
-
phaseStarted = performance.now();
|
|
569
|
-
if (reusableBuild) {
|
|
570
|
-
console.error(`[supercov] reusing exact-fingerprint instrumented build ${buildCacheKey.slice(0, 12)}`);
|
|
571
|
-
buildResult = { status: 0, signal: null };
|
|
572
|
-
}
|
|
573
|
-
else if (project.buildAdapter === "direct") {
|
|
574
|
-
instrumentDirectWorkspace(isolatedRoot, project.sourceFiles, manifestPath, project.sourceScope, project.sourceLimitations);
|
|
575
|
-
buildResult = { status: 0, signal: null };
|
|
576
|
-
}
|
|
577
|
-
else if (project.buildAdapter === "generic") {
|
|
578
|
-
instrumentDirectWorkspace(isolatedRoot, project.sourceFiles, manifestPath, project.sourceScope, project.sourceLimitations, project.usesJest ? "global" : "module", project.sourceRoots);
|
|
579
|
-
buildResult = await runChild(project.buildCommand[0], project.buildCommand.slice(1), {
|
|
580
|
-
cwd: isolatedRoot,
|
|
581
|
-
env: {
|
|
582
|
-
...coverageEnv,
|
|
583
|
-
...project.buildEnvironment,
|
|
584
|
-
NODE_ENV: "production",
|
|
585
|
-
...(project.usesJest ? { NODE_OPTIONS: testNodeOptions } : {}),
|
|
586
|
-
},
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
buildResult = await runChild(project.buildCommand[0], [
|
|
591
|
-
...project.buildCommand.slice(1),
|
|
592
|
-
"--",
|
|
593
|
-
"--config",
|
|
594
|
-
".supercov/vite.config.mjs",
|
|
595
|
-
], {
|
|
596
|
-
cwd: isolatedRoot,
|
|
597
|
-
env: {
|
|
598
|
-
...coverageEnv,
|
|
599
|
-
...project.buildEnvironment,
|
|
600
|
-
NODE_ENV: "production",
|
|
601
|
-
},
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
if (buildResult.status === 0 &&
|
|
605
|
-
project.buildAdapter !== "direct" &&
|
|
606
|
-
!reusableBuild) {
|
|
607
|
-
writeInstrumentedBuildCache(isolatedRoot, buildCacheKey);
|
|
608
|
-
}
|
|
609
|
-
timings.instrumentedBuildMs = performance.now() - phaseStarted;
|
|
610
|
-
if (receivedSignal)
|
|
611
|
-
throw new Error(`Interrupted by ${receivedSignal}`);
|
|
612
|
-
if (buildResult.error)
|
|
613
|
-
throw buildResult.error;
|
|
614
|
-
if (buildResult.status === 0) {
|
|
615
|
-
const assertionCount = instrumentNodeAssertionsInWorkspace(isolatedRoot, project.sourceScope.entries.map((entry) => entry.file), [project.playwrightModule]);
|
|
616
|
-
if (assertionCount > 0)
|
|
617
|
-
console.error(`[supercov] attributed ${assertionCount} native node:assert call(s)`);
|
|
618
|
-
updateRunState(root, runId, { status: "testing" });
|
|
619
|
-
const innerCoverageTool = expandedCommand(root, command)
|
|
620
|
-
.split(/\s+/)
|
|
621
|
-
.find((part) => /(?:^|\/)(?:c8|nyc)$/.test(part));
|
|
622
|
-
if (innerCoverageTool)
|
|
623
|
-
console.error(`[supercov] the wrapped command runs ${innerCoverageTool} over Supercov-instrumented code; its percentages and thresholds are not meaningful there. Wrap the underlying test command instead (for example: supercov -- node --test).`);
|
|
624
|
-
console.error(`[supercov] running in isolated workspace: ${command.join(" ")}`);
|
|
625
|
-
phaseStarted = performance.now();
|
|
626
|
-
testResult = await runChild(command[0], command.slice(1), {
|
|
627
|
-
cwd: isolatedRoot,
|
|
628
|
-
env: {
|
|
629
|
-
...coverageEnv,
|
|
630
|
-
NODE_OPTIONS: testNodeOptions,
|
|
631
|
-
SUPERCOV_CJS_INTERCEPT: "1",
|
|
632
|
-
},
|
|
633
|
-
});
|
|
634
|
-
timings.testCommandMs = performance.now() - phaseStarted;
|
|
635
|
-
if (receivedSignal)
|
|
636
|
-
throw new Error(`Interrupted by ${receivedSignal}`);
|
|
637
|
-
if (testResult.error)
|
|
638
|
-
throw testResult.error;
|
|
639
|
-
updateRunState(root, runId, { status: "publishing" });
|
|
640
|
-
phaseStarted = performance.now();
|
|
641
|
-
removeStoredTreeDeferred(root, persistedEvidenceDirectory);
|
|
642
|
-
spawnTrashDeleter(root);
|
|
643
|
-
if (existsSync(isolatedEvidenceDirectory))
|
|
644
|
-
atomicRenameSync(isolatedEvidenceDirectory, persistedEvidenceDirectory);
|
|
645
|
-
try {
|
|
646
|
-
removeStoredTreeDeferred(root, runStagingDirectory);
|
|
647
|
-
spawnTrashDeleter(root);
|
|
648
|
-
const evidenceArchivePath = resolve(runStagingDirectory, "evidence.raw.gz");
|
|
649
|
-
const rawEvidence = writeEngineEvidenceArchive([
|
|
650
|
-
{ file: manifestPath, path: "manifest.json" },
|
|
651
|
-
{ directory: persistedEvidenceDirectory },
|
|
652
|
-
{
|
|
653
|
-
directory: resolve(serverEvidenceRoot, runId),
|
|
654
|
-
prefix: "server",
|
|
655
|
-
},
|
|
656
|
-
], evidenceArchivePath);
|
|
657
|
-
timings.evidencePublicationMs = performance.now() - phaseStarted;
|
|
658
|
-
atomicWriteFileSync(resolve(runStagingDirectory, "run.json"), `${JSON.stringify({
|
|
659
|
-
id: runId,
|
|
660
|
-
startedAt,
|
|
661
|
-
durationMs: Date.now() - runStartedAt,
|
|
662
|
-
command,
|
|
663
|
-
testExitCode: testResult.timedOut
|
|
664
|
-
? COMMAND_TIMEOUT_EXIT_CODE
|
|
665
|
-
: testResult.status,
|
|
666
|
-
integrity: runIntegrity,
|
|
667
|
-
rawEvidence,
|
|
668
|
-
isolatedBuild: true,
|
|
669
|
-
instrumentedBuildCache: {
|
|
670
|
-
key: buildCacheKey,
|
|
671
|
-
reused: Boolean(reusableBuild),
|
|
672
|
-
},
|
|
673
|
-
timings: roundedTimings(timings),
|
|
674
|
-
}, null, 2)}\n`);
|
|
675
|
-
atomicRenameSync(runStagingDirectory, storedRunDirectory);
|
|
676
|
-
runPublished = true;
|
|
677
|
-
console.log(`[coverage] evidence: ${resolve(storedRunDirectory, "evidence.raw.gz")}`);
|
|
678
|
-
}
|
|
679
|
-
catch (error) {
|
|
680
|
-
timings.evidencePublicationMs = performance.now() - phaseStarted;
|
|
681
|
-
publicationFailed = true;
|
|
682
|
-
console.error("[supercov] failed to publish coverage evidence", error);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
const resultCode = exitCode(buildResult) || exitCode(testResult) || (publicationFailed ? 1 : 0);
|
|
686
|
-
updateRunState(root, runId, { status: resultCode === 0 ? "complete" : "failed" });
|
|
687
|
-
if (runPublished && !finalizePublishedRunStorage(root, runId))
|
|
688
|
-
throw new Error(`Published run ${runId} is missing a durable artifact`);
|
|
689
|
-
console.error(`[supercov] timings ${formatTimings(timings, performance.now() - runStartedMonotonic)}`);
|
|
690
|
-
timingsPrinted = true;
|
|
691
|
-
return resultCode;
|
|
692
|
-
}
|
|
693
|
-
catch (error) {
|
|
694
|
-
const status = receivedSignal ? "interrupted" : "failed";
|
|
695
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
696
|
-
try {
|
|
697
|
-
updateRunState(root, runId, {
|
|
698
|
-
status,
|
|
699
|
-
...(receivedSignal ? { signal: receivedSignal } : {}),
|
|
700
|
-
error: message,
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
catch {
|
|
704
|
-
// The original error remains authoritative.
|
|
705
|
-
}
|
|
706
|
-
if (!receivedSignal)
|
|
707
|
-
console.error(`[supercov] ${process.env["SUPERCOV_DEBUG"] && error instanceof Error ? error.stack ?? message : message}`);
|
|
708
|
-
return receivedSignal ? signalExitCode(receivedSignal) : 1;
|
|
709
|
-
}
|
|
710
|
-
finally {
|
|
711
|
-
if (!timingsPrinted)
|
|
712
|
-
console.error(`[supercov] timings ${formatTimings(timings, performance.now() - runStartedMonotonic)}`);
|
|
713
|
-
if (signalEscalation) {
|
|
714
|
-
clearTimeout(signalEscalation);
|
|
715
|
-
signalEscalation = undefined;
|
|
716
|
-
}
|
|
717
|
-
for (const [signal, handler] of signalHandlers)
|
|
718
|
-
process.removeListener(signal, handler);
|
|
719
|
-
try {
|
|
720
|
-
removeStoredTreeDeferred(root, runStagingDirectory);
|
|
721
|
-
removeStoredTreeDeferred(root, resolve(serverEvidenceRoot, runId));
|
|
722
|
-
// The stable isolated namespace persists as a build/snapshot cache at a
|
|
723
|
-
// stable path, but its copied source and test files must not: ordinary
|
|
724
|
-
// runner discovery at the project root would double-count the suite.
|
|
725
|
-
if (!process.env["SUPERCOV_KEEP_WORKSPACE"])
|
|
726
|
-
pruneCachedWorkspaceSources(root);
|
|
727
|
-
spawnTrashDeleter(root);
|
|
728
|
-
}
|
|
729
|
-
catch (error) {
|
|
730
|
-
console.error(`[supercov] isolated workspace cleanup failed: ${String(error)}`);
|
|
731
|
-
}
|
|
732
|
-
lock.release();
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
const commandArgs = process.argv.slice(2);
|
|
736
|
-
const rawArgs = commandArgs[0] === "--help" || commandArgs[0] === "-h"
|
|
737
|
-
? ["help", ...commandArgs.slice(1)]
|
|
738
|
-
: commandArgs;
|
|
739
|
-
const queryCommand = rawArgs[0];
|
|
740
|
-
const wantsJson = rawArgs.includes("--json");
|
|
741
|
-
const agentCommand = (() => {
|
|
742
|
-
if (queryCommand !== "runs" || rawArgs[2] !== "coverage")
|
|
743
|
-
return queryCommand;
|
|
744
|
-
const child = rawArgs[3];
|
|
745
|
-
return `coverage.${child && !child.startsWith("-") ? child : "summary"}`;
|
|
746
|
-
})();
|
|
747
|
-
const reportCliError = (error) => {
|
|
748
|
-
if (wantsJson)
|
|
749
|
-
process.stdout.write(agentFailureJson(error, agentCommand));
|
|
750
|
-
else
|
|
751
|
-
console.error(`[supercov] ${error instanceof Error ? error.message : String(error)}`);
|
|
752
|
-
process.exitCode = 2;
|
|
753
|
-
};
|
|
754
|
-
if (queryCommand === "clean" || queryCommand === "prune") {
|
|
755
|
-
try {
|
|
756
|
-
if (queryCommand === "clean")
|
|
757
|
-
cleanCommand(rawArgs.slice(1));
|
|
758
|
-
else
|
|
759
|
-
pruneCommand(rawArgs.slice(1));
|
|
760
|
-
}
|
|
761
|
-
catch (error) {
|
|
762
|
-
reportCliError(error);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
else if (queryCommand === "merge") {
|
|
766
|
-
try {
|
|
767
|
-
const mergedRunId = mergeCoverageRuns(process.cwd(), rawArgs.slice(1));
|
|
768
|
-
console.log(`[supercov] merged run ${mergedRunId}`);
|
|
769
|
-
console.log(`npx supercov runs ${mergedRunId} coverage`);
|
|
770
|
-
}
|
|
771
|
-
catch (error) {
|
|
772
|
-
reportCliError(error);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
else if (queryCommand && coverageQueryCommands.has(queryCommand)) {
|
|
776
|
-
try {
|
|
777
|
-
await runQueryCommand(queryCommand, rawArgs.slice(1));
|
|
778
|
-
}
|
|
779
|
-
catch (error) {
|
|
780
|
-
reportCliError(error);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
else if (rawArgs[0] && rawArgs[0] !== "--") {
|
|
784
|
-
reportCliError(new SupercovError("UNKNOWN_COMMAND", `Unknown command: ${rawArgs[0]}. Try supercov help.`, { details: { command: rawArgs[0] } }));
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
const separator = process.argv.indexOf("--");
|
|
788
|
-
const command = separator >= 0 ? process.argv.slice(separator + 1) : [];
|
|
789
|
-
if (command.length === 0) {
|
|
790
|
-
console.error("Usage: supercov -- <test command>");
|
|
791
|
-
process.exitCode = 2;
|
|
792
|
-
}
|
|
793
|
-
else {
|
|
794
|
-
process.exitCode = await createCoverageRun(command);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
//# sourceMappingURL=cli.js.map
|