supercov 0.0.0

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/src/cli.ts ADDED
@@ -0,0 +1,336 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import {
4
+ copyFileSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { dirname, relative, resolve, sep } from "node:path";
11
+ import { fileURLToPath, pathToFileURL } from "node:url";
12
+ import { writeMcdcReport } from "./reporter.ts";
13
+ import { coverageQueryCommands, runQueryCommand } from "./query.ts";
14
+ import { discoverCoverageProject } from "./project.ts";
15
+ import { createRunIntegrity } from "./integrity.ts";
16
+
17
+ const commandArgs = process.argv.slice(2);
18
+ const rawArgs =
19
+ commandArgs[0] === "--help" || commandArgs[0] === "-h"
20
+ ? ["help", ...commandArgs.slice(1)]
21
+ : commandArgs;
22
+ const queryCommand = rawArgs[0];
23
+
24
+ if (queryCommand && coverageQueryCommands.has(queryCommand)) {
25
+ try {
26
+ await runQueryCommand(queryCommand, rawArgs.slice(1));
27
+ } catch (error) {
28
+ console.error(
29
+ `[supercov] ${error instanceof Error ? error.message : String(error)}`,
30
+ );
31
+ process.exitCode = 2;
32
+ }
33
+ } else if (rawArgs[0] && rawArgs[0] !== "--") {
34
+ console.error(
35
+ `[supercov] Unknown command: ${rawArgs[0]}. Try supercov help.`,
36
+ );
37
+ process.exitCode = 2;
38
+ } else {
39
+ const separator = process.argv.indexOf("--");
40
+ const command = separator >= 0 ? process.argv.slice(separator + 1) : [];
41
+ if (command.length === 0) {
42
+ console.error("Usage: supercov -- <test command>");
43
+ process.exit(2);
44
+ }
45
+
46
+ const root = process.cwd();
47
+ const project = discoverCoverageProject(root);
48
+ const runId = new Date().toISOString().replace(/[:.]/g, "-");
49
+ const runStartedAt = Date.now();
50
+ const generatedDirectory = resolve(root, ".supercov");
51
+ const evidenceDirectoryRelative = `.supercov/evidence/${runId}`;
52
+ const evidenceDirectory = resolve(root, evidenceDirectoryRelative);
53
+ const generatedPlaywrightConfig = resolve(
54
+ generatedDirectory,
55
+ "playwright.config.mjs",
56
+ );
57
+ const generatedViteConfig = resolve(generatedDirectory, "vite.config.ts");
58
+ const generatedVitestConfig = resolve(generatedDirectory, "vitest.config.ts");
59
+ const manifestRelative = `.supercov/work/${runId}/mcdc-manifest.json`;
60
+ const manifestPath = resolve(root, manifestRelative);
61
+ const packageSource = fileURLToPath(new URL(".", import.meta.url));
62
+ const runIntegrity = createRunIntegrity(root, project, packageSource);
63
+ mkdirSync(generatedDirectory, { recursive: true });
64
+ mkdirSync(evidenceDirectory, { recursive: true });
65
+ for (const file of [
66
+ "playwright.ts",
67
+ "playwrightReporter.ts",
68
+ "provenance.ts",
69
+ "register.mjs",
70
+ "resolve-loader.mjs",
71
+ "transport.ts",
72
+ "types.ts",
73
+ ]) {
74
+ copyFileSync(
75
+ resolve(packageSource, file),
76
+ resolve(generatedDirectory, file),
77
+ );
78
+ }
79
+ const generatedPlaywrightAdapter = resolve(
80
+ generatedDirectory,
81
+ "playwright.ts",
82
+ );
83
+ const generatedPlaywrightReporter = resolve(
84
+ generatedDirectory,
85
+ "playwrightReporter.ts",
86
+ );
87
+ writeFileSync(
88
+ generatedPlaywrightAdapter,
89
+ readFileSync(generatedPlaywrightAdapter, "utf8")
90
+ .replace(
91
+ "__SUPERCOV_EVIDENCE_DIRECTORY__",
92
+ evidenceDirectoryRelative,
93
+ )
94
+ .replace(
95
+ "__SUPERCOV_PLAYWRIGHT_MODULE__",
96
+ project.playwrightModule,
97
+ )
98
+ .replace(
99
+ "__SUPERCOV_RUN_ID__",
100
+ runId,
101
+ ),
102
+ );
103
+ writeFileSync(
104
+ generatedPlaywrightReporter,
105
+ readFileSync(generatedPlaywrightReporter, "utf8").replace(
106
+ "__SUPERCOV_EVIDENCE_DIRECTORY__",
107
+ evidenceDirectoryRelative,
108
+ ),
109
+ );
110
+ const generatedResolveLoader = resolve(
111
+ generatedDirectory,
112
+ "resolve-loader.mjs",
113
+ );
114
+ writeFileSync(
115
+ generatedResolveLoader,
116
+ readFileSync(generatedResolveLoader, "utf8").replace(
117
+ "__SUPERCOV_PLAYWRIGHT_MODULE__",
118
+ project.playwrightModule,
119
+ ),
120
+ );
121
+ if (project.playwrightConfig) {
122
+ const configImport = project.essentialOffline
123
+ ? `../${relative(root, project.playwrightConfig).split(sep).join("/")}`
124
+ : pathToFileURL(project.playwrightConfig).href;
125
+ writeFileSync(
126
+ generatedPlaywrightConfig,
127
+ [
128
+ `import './register.mjs';`,
129
+ `import { isAbsolute, resolve } from 'node:path';`,
130
+ `import original from '${configImport}';`,
131
+ `const resolved = typeof original === 'function' ? await original({ command: 'test', mode: 'test' }) : original;`,
132
+ `const originalDirectory = ${JSON.stringify(dirname(project.playwrightConfig))};`,
133
+ `const absoluteFromOriginal = value => value && !isAbsolute(value) ? resolve(originalDirectory, value) : value;`,
134
+ `const normalizeWebServer = server => server ? ({ ...server, cwd: absoluteFromOriginal(server.cwd ?? originalDirectory) }) : server;`,
135
+ `const normalized = { ...resolved,`,
136
+ ` testDir: absoluteFromOriginal(resolved?.testDir),`,
137
+ ` outputDir: absoluteFromOriginal(resolved?.outputDir),`,
138
+ ` snapshotDir: absoluteFromOriginal(resolved?.snapshotDir),`,
139
+ ` projects: resolved?.projects?.map(project => ({ ...project, testDir: absoluteFromOriginal(project.testDir), outputDir: absoluteFromOriginal(project.outputDir), snapshotDir: absoluteFromOriginal(project.snapshotDir) })),`,
140
+ ` webServer: Array.isArray(resolved?.webServer) ? resolved.webServer.map(normalizeWebServer) : normalizeWebServer(resolved?.webServer),`,
141
+ `};`,
142
+ `const configuredReporters = normalized.reporter;`,
143
+ `const reporters = configuredReporters`,
144
+ ` ? (typeof configuredReporters === 'string' ? [[configuredReporters]] : (Array.isArray(configuredReporters[0]) ? configuredReporters : [configuredReporters]))`,
145
+ ` : [['list']];`,
146
+ `const coverageReporter = process.env.TEST_IN_CONTAINER === 'true'`,
147
+ ` ? '/workspace/.supercov/playwrightReporter.ts'`,
148
+ ` : ${JSON.stringify(resolve(packageSource, "playwrightReporter.ts"))};`,
149
+ `export default { ...normalized, reporter: [...reporters, [coverageReporter]] };`,
150
+ "",
151
+ ].join("\n"),
152
+ );
153
+ }
154
+ writeFileSync(
155
+ generatedViteConfig,
156
+ [
157
+ `import { loadConfigFromFile, mergeConfig } from 'vite';`,
158
+ `import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.ts")).href}';`,
159
+ `export default async function supercovViteConfig(env) {`,
160
+ ` const loaded = await loadConfigFromFile(env, undefined, process.cwd());`,
161
+ ` return mergeConfig(loaded?.config ?? {}, { plugins: [mcdcVitePlugin(${JSON.stringify({ root, sourceRoots: project.sourceRoots, manifestPath })})] });`,
162
+ `}`,
163
+ "",
164
+ ].join("\n"),
165
+ );
166
+ writeFileSync(
167
+ generatedVitestConfig,
168
+ [
169
+ `import { loadConfigFromFile, mergeConfig } from 'vite';`,
170
+ `import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.ts")).href}';`,
171
+ `import SupercovVitestReporter from '${pathToFileURL(resolve(packageSource, "vitestReporter.ts")).href}';`,
172
+ `const discoveredConfig = ${JSON.stringify(project.vitestConfig)};`,
173
+ `export default async function supercovVitestConfig(env) {`,
174
+ ` const originalPath = process.env.SUPERCOV_ORIGINAL_VITEST_CONFIG || discoveredConfig;`,
175
+ ` const loaded = originalPath ? await loadConfigFromFile(env, originalPath, process.cwd()) : undefined;`,
176
+ ` const config = mergeConfig(loaded?.config ?? {}, {`,
177
+ ` plugins: [mcdcVitePlugin(${JSON.stringify({ root, sourceRoots: project.sourceRoots, manifestPath })})],`,
178
+ ` test: { setupFiles: [${JSON.stringify(resolve(packageSource, "vitest.ts"))}], maxConcurrency: 1 },`,
179
+ ` });`,
180
+ ` const configuredReporters = loaded?.config?.test?.reporters;`,
181
+ ` config.test ??= {};`,
182
+ ` config.test.reporters = configuredReporters`,
183
+ ` ? [...(Array.isArray(configuredReporters) ? configuredReporters : [configuredReporters]), new SupercovVitestReporter()]`,
184
+ ` : ['default', new SupercovVitestReporter()];`,
185
+ ` return config;`,
186
+ `}`,
187
+ "",
188
+ ].join("\n"),
189
+ );
190
+ const coverageEnv = {
191
+ ...process.env,
192
+ TEST_MCDC: "true",
193
+ TEST_OFFLINE_RESULTS_RUN_ID: runId,
194
+ SUPERCOV_EVIDENCE_DIR: evidenceDirectoryRelative,
195
+ SUPERCOV_RUN_ID: runId,
196
+ SUPERCOV_MANIFEST: manifestRelative,
197
+ SUPERCOV_PLAYWRIGHT_MODULE: project.playwrightModule,
198
+ SUPERCOV_PROJECT_ROOT: root,
199
+ SUPERCOV_GENERATED_VITEST_CONFIG: generatedVitestConfig,
200
+ SUPERCOV_GENERATED_PLAYWRIGHT_CONFIG: generatedPlaywrightConfig,
201
+ };
202
+ const testNodeOptions = [
203
+ process.env["NODE_OPTIONS"],
204
+ `--import=${import.meta.resolve("tsx")}`,
205
+ `--import=${pathToFileURL(resolve(generatedDirectory, "register.mjs")).href}`,
206
+ ]
207
+ .filter(Boolean)
208
+ .join(" ");
209
+
210
+ console.error(`[supercov] instrumenting ${root}`);
211
+ const instrumentedBuild = spawnSync(
212
+ project.buildCommand[0]!,
213
+ [
214
+ ...project.buildCommand.slice(1),
215
+ "--",
216
+ "--config",
217
+ ".supercov/vite.config.ts",
218
+ ],
219
+ {
220
+ cwd: root,
221
+ env: {
222
+ ...coverageEnv,
223
+ ...(project.essentialOffline ? { TEST_OFFLINE: "true" } : {}),
224
+ NODE_ENV: "production",
225
+ },
226
+ stdio: "inherit",
227
+ },
228
+ );
229
+
230
+ let testRun: ReturnType<typeof spawnSync> | undefined;
231
+ let reportFailed = false;
232
+
233
+ if (!instrumentedBuild.error && instrumentedBuild.status === 0) {
234
+ // The Essential offline VM pool keys its snapshot on dependency overlays,
235
+ // not the mounted application build. A tiny build-hash overlay invalidates
236
+ // the warm snapshot exactly when the instrumented bundle changes. Other
237
+ // runners simply ignore these environment variables.
238
+ const overlayPackage = "essential-seo/.mcdc-pool";
239
+ if (project.essentialOffline) {
240
+ const overlayDist = resolve(root, ".mcdc-pool/dist");
241
+ const overlayMarker = resolve(overlayDist, "instrumented-build.sha256");
242
+ const bundleHash = createHash("sha256")
243
+ .update(readFileSync(resolve(root, "build/server/index.js")))
244
+ .update(readFileSync(manifestPath))
245
+ .digest("hex");
246
+ mkdirSync(overlayDist, { recursive: true });
247
+ if (
248
+ !existsSync(overlayMarker) ||
249
+ readFileSync(overlayMarker, "utf8").trim() !== bundleHash
250
+ ) {
251
+ writeFileSync(overlayMarker, `${bundleHash}\n`);
252
+ }
253
+ }
254
+
255
+ console.error(`[supercov] running: ${command.join(" ")}`);
256
+ testRun = spawnSync(command[0]!, command.slice(1), {
257
+ cwd: root,
258
+ stdio: "inherit",
259
+ env: {
260
+ ...coverageEnv,
261
+ NODE_OPTIONS: testNodeOptions,
262
+ ...(!project.essentialOffline
263
+ ? {
264
+ SUPERCOV_CJS_INTERCEPT: "1",
265
+ }
266
+ : {}),
267
+ ...(project.essentialOffline
268
+ ? {
269
+ TEST_OFFLINE_LOCAL_OVERLAY: "true",
270
+ TEST_OFFLINE_LOCAL_OVERLAY_PKGS: overlayPackage,
271
+ TEST_PLAYWRIGHT_CONFIG:
272
+ ".supercov/playwright.config.mjs",
273
+ }
274
+ : {}),
275
+ },
276
+ });
277
+
278
+ try {
279
+ writeMcdcReport(
280
+ evidenceDirectory,
281
+ runId,
282
+ runStartedAt,
283
+ manifestPath,
284
+ testRun.status,
285
+ runIntegrity,
286
+ );
287
+ const storedRunDirectory = resolve(
288
+ root,
289
+ ".supercov/runs",
290
+ runId,
291
+ );
292
+ writeFileSync(
293
+ resolve(storedRunDirectory, "run.json"),
294
+ `${JSON.stringify(
295
+ {
296
+ id: runId,
297
+ startedAt: new Date(runStartedAt).toISOString(),
298
+ durationMs: Date.now() - runStartedAt,
299
+ command,
300
+ testExitCode: testRun.status,
301
+ integrity: runIntegrity,
302
+ },
303
+ null,
304
+ 2,
305
+ )}\n`,
306
+ );
307
+ } catch (error) {
308
+ reportFailed = true;
309
+ console.error("[supercov] failed to generate report", error);
310
+ }
311
+ }
312
+
313
+ console.error("[supercov] restoring the ordinary build");
314
+ const restore = spawnSync(
315
+ project.buildCommand[0]!,
316
+ project.buildCommand.slice(1),
317
+ {
318
+ cwd: root,
319
+ env: {
320
+ ...process.env,
321
+ ...(project.essentialOffline ? { TEST_OFFLINE: "true" } : {}),
322
+ NODE_ENV: "production",
323
+ },
324
+ stdio: "inherit",
325
+ },
326
+ );
327
+
328
+ if (instrumentedBuild.error) throw instrumentedBuild.error;
329
+ if (testRun?.error) throw testRun.error;
330
+ if (restore.error) throw restore.error;
331
+ process.exitCode =
332
+ instrumentedBuild.status ||
333
+ testRun?.status ||
334
+ restore.status ||
335
+ (reportFailed ? 1 : 0);
336
+ }