supercov 0.0.2 → 0.0.3
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/LICENSE +21 -0
- package/README.md +115 -20
- package/bin/supercov.js +10 -0
- package/dist/atomic.d.ts +6 -0
- package/dist/atomic.d.ts.map +1 -0
- package/dist/atomic.js +47 -0
- package/dist/atomic.js.map +1 -0
- package/dist/cli.js +431 -214
- package/dist/cli.js.map +1 -1
- package/dist/directInstrumenter.d.ts +8 -0
- package/dist/directInstrumenter.d.ts.map +1 -0
- package/dist/directInstrumenter.js +70 -0
- package/dist/directInstrumenter.js.map +1 -0
- package/dist/instrumenter.d.ts.map +1 -1
- package/dist/instrumenter.js +251 -41
- package/dist/instrumenter.js.map +1 -1
- package/dist/integrity.d.ts.map +1 -1
- package/dist/integrity.js +27 -0
- package/dist/integrity.js.map +1 -1
- package/dist/launchSupervisor.d.ts +24 -0
- package/dist/launchSupervisor.d.ts.map +1 -0
- package/dist/launchSupervisor.js +397 -0
- package/dist/launchSupervisor.js.map +1 -0
- package/dist/playwright.d.ts +0 -4
- package/dist/playwright.d.ts.map +1 -1
- package/dist/playwright.js +33 -17
- package/dist/playwright.js.map +1 -1
- package/dist/playwrightReporter.d.ts.map +1 -1
- package/dist/playwrightReporter.js +3 -2
- package/dist/playwrightReporter.js.map +1 -1
- package/dist/project.d.ts +5 -2
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +192 -27
- package/dist/project.js.map +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +1 -0
- package/dist/query.js.map +1 -1
- package/dist/register.mjs +18 -13
- package/dist/register.mjs.map +1 -1
- package/dist/reporter.d.ts +4 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +13 -11
- package/dist/reporter.js.map +1 -1
- package/dist/resolve-loader.d.mts.map +1 -1
- package/dist/resolve-loader.mjs +11 -3
- package/dist/resolve-loader.mjs.map +1 -1
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +35 -7
- package/dist/runtime.js.map +1 -1
- package/dist/transport.d.ts +2 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +2 -0
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vitePlugin.d.ts.map +1 -1
- package/dist/vitePlugin.js +4 -3
- package/dist/vitePlugin.js.map +1 -1
- package/dist/vitest.js +3 -2
- package/dist/vitest.js.map +1 -1
- package/dist/vitestReporter.d.ts +17 -2
- package/dist/vitestReporter.d.ts.map +1 -1
- package/dist/vitestReporter.js +51 -2
- package/dist/vitestReporter.js.map +1 -1
- package/dist/workspace.d.ts +46 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +284 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +19 -5
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,441 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { dirname, relative, resolve, sep } from "node:path";
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, } from "node:fs";
|
|
3
|
+
import { relative, resolve, sep } from "node:path";
|
|
5
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { atomicRenameSync, atomicWriteFileSync } from "./atomic.js";
|
|
6
6
|
import { writeMcdcReport } from "./reporter.js";
|
|
7
7
|
import { coverageQueryCommands, runQueryCommand } from "./query.js";
|
|
8
8
|
import { discoverCoverageProject } from "./project.js";
|
|
9
9
|
import { createRunIntegrity } from "./integrity.js";
|
|
10
|
+
import { instrumentDirectWorkspace } from "./directInstrumenter.js";
|
|
11
|
+
import { acquireProjectLock, cachedWorkspacePath, cleanCoverageStorage, prepareCachedWorkspace, recoverAbandonedRuns, updateRunState, writeRunState, } from "./workspace.js";
|
|
12
|
+
let activeChild;
|
|
13
|
+
let signalEscalation;
|
|
14
|
+
function terminateChild(signal) {
|
|
15
|
+
const child = activeChild;
|
|
16
|
+
if (!child?.pid)
|
|
17
|
+
return;
|
|
18
|
+
try {
|
|
19
|
+
if (process.platform !== "win32")
|
|
20
|
+
process.kill(-child.pid, signal);
|
|
21
|
+
else
|
|
22
|
+
child.kill(signal);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
try {
|
|
26
|
+
child.kill(signal);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// The child may have exited between the signal and cleanup.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function runChild(command, args, options) {
|
|
34
|
+
return new Promise((resolveChild) => {
|
|
35
|
+
let error;
|
|
36
|
+
const child = spawn(command, args, {
|
|
37
|
+
cwd: options.cwd,
|
|
38
|
+
env: options.env,
|
|
39
|
+
stdio: "inherit",
|
|
40
|
+
detached: process.platform !== "win32",
|
|
41
|
+
});
|
|
42
|
+
activeChild = child;
|
|
43
|
+
child.once("error", (failure) => {
|
|
44
|
+
error = failure;
|
|
45
|
+
});
|
|
46
|
+
child.once("close", (status, signal) => {
|
|
47
|
+
if (activeChild === child)
|
|
48
|
+
activeChild = undefined;
|
|
49
|
+
resolveChild({ status, signal, ...(error ? { error } : {}) });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function exitCode(result) {
|
|
54
|
+
if (!result)
|
|
55
|
+
return 0;
|
|
56
|
+
if (result.error)
|
|
57
|
+
return 1;
|
|
58
|
+
if (result.status !== null)
|
|
59
|
+
return result.status;
|
|
60
|
+
return result.signal ? 128 : 1;
|
|
61
|
+
}
|
|
62
|
+
function signalExitCode(signal) {
|
|
63
|
+
if (signal === "SIGHUP")
|
|
64
|
+
return 129;
|
|
65
|
+
if (signal === "SIGINT")
|
|
66
|
+
return 130;
|
|
67
|
+
if (signal === "SIGTERM")
|
|
68
|
+
return 143;
|
|
69
|
+
return 128;
|
|
70
|
+
}
|
|
71
|
+
function parseCleanOptions(args) {
|
|
72
|
+
let keep = 20;
|
|
73
|
+
let dryRun = false;
|
|
74
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
75
|
+
const argument = args[index];
|
|
76
|
+
if (argument === "--dry-run")
|
|
77
|
+
dryRun = true;
|
|
78
|
+
else if (argument === "--keep") {
|
|
79
|
+
const value = Number(args[++index]);
|
|
80
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
81
|
+
throw new Error("--keep must be a non-negative integer");
|
|
82
|
+
keep = value;
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
throw new Error(`Unknown clean option: ${argument}`);
|
|
86
|
+
}
|
|
87
|
+
return { keep, dryRun };
|
|
88
|
+
}
|
|
89
|
+
function cleanCommand(args) {
|
|
90
|
+
const options = parseCleanOptions(args);
|
|
91
|
+
const result = cleanCoverageStorage(process.cwd(), options);
|
|
92
|
+
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)`);
|
|
93
|
+
for (const id of result.removedRuns)
|
|
94
|
+
console.log(id);
|
|
95
|
+
}
|
|
96
|
+
async function createCoverageRun(command) {
|
|
97
|
+
const root = process.cwd();
|
|
98
|
+
const runId = new Date().toISOString().replace(/[:.]/g, "-");
|
|
99
|
+
const runStartedAt = Date.now();
|
|
100
|
+
const recovered = recoverAbandonedRuns(root);
|
|
101
|
+
if (recovered.length > 0)
|
|
102
|
+
console.error(`[supercov] recovered abandoned run(s): ${recovered.join(", ")}`);
|
|
103
|
+
const lock = acquireProjectLock(root, runId);
|
|
104
|
+
const project = discoverCoverageProject(root, process.env, command);
|
|
105
|
+
const packageSource = fileURLToPath(new URL(".", import.meta.url));
|
|
106
|
+
const runIntegrity = createRunIntegrity(root, project, packageSource);
|
|
107
|
+
const workspace = cachedWorkspacePath(root);
|
|
108
|
+
const reportStagingDirectory = resolve(root, ".supercov/work", runId, "report-publication");
|
|
109
|
+
const storedRunDirectory = resolve(root, ".supercov/runs", runId);
|
|
110
|
+
const startedAt = new Date(runStartedAt).toISOString();
|
|
111
|
+
writeRunState(root, runId, {
|
|
112
|
+
id: runId,
|
|
113
|
+
pid: process.pid,
|
|
114
|
+
root,
|
|
115
|
+
workspace,
|
|
116
|
+
startedAt,
|
|
117
|
+
status: "preparing",
|
|
118
|
+
});
|
|
119
|
+
let receivedSignal;
|
|
120
|
+
const signalHandlers = new Map();
|
|
121
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
122
|
+
const handler = () => {
|
|
123
|
+
if (receivedSignal)
|
|
124
|
+
return;
|
|
125
|
+
receivedSignal = signal;
|
|
126
|
+
try {
|
|
127
|
+
updateRunState(root, runId, { status: "interrupted", signal });
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// State recovery on the next invocation remains the fallback.
|
|
131
|
+
}
|
|
132
|
+
terminateChild(signal);
|
|
133
|
+
signalEscalation = setTimeout(() => terminateChild("SIGKILL"), 5_000);
|
|
134
|
+
signalEscalation.unref();
|
|
135
|
+
};
|
|
136
|
+
signalHandlers.set(signal, handler);
|
|
137
|
+
process.once(signal, handler);
|
|
138
|
+
}
|
|
139
|
+
let buildResult;
|
|
140
|
+
let testResult;
|
|
141
|
+
let reportFailed = false;
|
|
142
|
+
try {
|
|
143
|
+
const isolatedRoot = prepareCachedWorkspace(root);
|
|
144
|
+
if (receivedSignal)
|
|
145
|
+
throw new Error(`Interrupted by ${receivedSignal}`);
|
|
146
|
+
const generatedDirectory = resolve(isolatedRoot, ".supercov");
|
|
147
|
+
const evidenceDirectoryRelative = `.supercov/evidence/${runId}`;
|
|
148
|
+
const isolatedEvidenceDirectory = resolve(isolatedRoot, evidenceDirectoryRelative);
|
|
149
|
+
const persistedEvidenceDirectory = resolve(root, ".supercov/evidence", runId);
|
|
150
|
+
const generatedPlaywrightConfig = resolve(generatedDirectory, "playwright.config.mjs");
|
|
151
|
+
const generatedViteConfig = resolve(generatedDirectory, "vite.config.mjs");
|
|
152
|
+
const generatedVitestConfig = resolve(generatedDirectory, "vitest.config.mjs");
|
|
153
|
+
const manifestPath = resolve(generatedDirectory, "manifest.json");
|
|
154
|
+
const isolatedPlaywrightConfig = project.playwrightConfig
|
|
155
|
+
? resolve(isolatedRoot, relative(root, project.playwrightConfig))
|
|
156
|
+
: undefined;
|
|
157
|
+
const isolatedVitestConfig = project.vitestConfig
|
|
158
|
+
? resolve(isolatedRoot, relative(root, project.vitestConfig))
|
|
159
|
+
: undefined;
|
|
160
|
+
mkdirSync(generatedDirectory, { recursive: true });
|
|
161
|
+
mkdirSync(isolatedEvidenceDirectory, { recursive: true });
|
|
162
|
+
for (const file of [
|
|
163
|
+
"atomic.js",
|
|
164
|
+
"launchSupervisor.js",
|
|
165
|
+
"playwright.js",
|
|
166
|
+
"playwrightReporter.js",
|
|
167
|
+
"provenance.js",
|
|
168
|
+
"register.mjs",
|
|
169
|
+
"resolve-loader.mjs",
|
|
170
|
+
"runtime.js",
|
|
171
|
+
"transport.js",
|
|
172
|
+
"types.js",
|
|
173
|
+
]) {
|
|
174
|
+
copyFileSync(resolve(packageSource, file), resolve(generatedDirectory, file));
|
|
175
|
+
}
|
|
176
|
+
const generatedPlaywrightAdapter = resolve(generatedDirectory, "playwright.js");
|
|
177
|
+
const generatedPlaywrightReporter = resolve(generatedDirectory, "playwrightReporter.js");
|
|
178
|
+
atomicWriteFileSync(generatedPlaywrightAdapter, readFileSync(generatedPlaywrightAdapter, "utf8")
|
|
179
|
+
.replace("__SUPERCOV_EVIDENCE_DIRECTORY__", evidenceDirectoryRelative)
|
|
180
|
+
.replace("__SUPERCOV_PLAYWRIGHT_MODULE__", project.playwrightModule)
|
|
181
|
+
.replace("__SUPERCOV_PLAYWRIGHT_TEST_EXPORT__", project.playwrightTestExport)
|
|
182
|
+
.replace("/*__SUPERCOV_ADAPTER_EXPORTS__*/", [
|
|
183
|
+
...(project.playwrightTestExport === "test"
|
|
184
|
+
? []
|
|
185
|
+
: [
|
|
186
|
+
`export { instrumentedTest as ${project.playwrightTestExport} };`,
|
|
187
|
+
]),
|
|
188
|
+
...project.playwrightExports
|
|
189
|
+
.filter((name) => name !== "test" &&
|
|
190
|
+
name !== "expect" &&
|
|
191
|
+
name !== project.playwrightTestExport)
|
|
192
|
+
.map((name) => `export const ${name} = adapter[${JSON.stringify(name)}];`),
|
|
193
|
+
].join("\n"))
|
|
194
|
+
.replace("__SUPERCOV_RUN_ID__", runId));
|
|
195
|
+
atomicWriteFileSync(generatedPlaywrightReporter, readFileSync(generatedPlaywrightReporter, "utf8").replace("__SUPERCOV_EVIDENCE_DIRECTORY__", evidenceDirectoryRelative));
|
|
196
|
+
const generatedResolveLoader = resolve(generatedDirectory, "resolve-loader.mjs");
|
|
197
|
+
atomicWriteFileSync(generatedResolveLoader, readFileSync(generatedResolveLoader, "utf8").replace("__SUPERCOV_PLAYWRIGHT_MODULE__", project.playwrightModule));
|
|
198
|
+
if (isolatedPlaywrightConfig) {
|
|
199
|
+
// Keep this import inside Playwright's own transform graph. In older
|
|
200
|
+
// supported releases, a native ESM file-URL import of a TypeScript
|
|
201
|
+
// config enters the synchronous transform bridge recursively and can
|
|
202
|
+
// deadlock in Atomics.wait before test discovery starts.
|
|
203
|
+
const configImport = `../${relative(isolatedRoot, isolatedPlaywrightConfig).split(sep).join("/")}`;
|
|
204
|
+
atomicWriteFileSync(generatedPlaywrightConfig, [
|
|
205
|
+
`import './register.mjs';`,
|
|
206
|
+
`import { dirname, isAbsolute, relative, resolve } from 'node:path';`,
|
|
207
|
+
`import { fileURLToPath } from 'node:url';`,
|
|
208
|
+
`import original from '${configImport}';`,
|
|
209
|
+
`const resolved = typeof original === 'function' ? await original({ command: 'test', mode: 'test' }) : original;`,
|
|
210
|
+
`const runtimeProjectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');`,
|
|
211
|
+
`const originalDirectory = dirname(fileURLToPath(new URL(${JSON.stringify(configImport)}, import.meta.url)));`,
|
|
212
|
+
`const sourceProjectRoot = process.env.SUPERCOV_SOURCE_PROJECT_ROOT;`,
|
|
213
|
+
`const runtimePath = value => {`,
|
|
214
|
+
` if (!value) return value;`,
|
|
215
|
+
` const absolute = isAbsolute(value) ? value : resolve(originalDirectory, value);`,
|
|
216
|
+
` const local = relative(runtimeProjectRoot, absolute);`,
|
|
217
|
+
` if (local === '' || (!local.startsWith('..') && !isAbsolute(local))) return absolute;`,
|
|
218
|
+
` if (sourceProjectRoot) {`,
|
|
219
|
+
` const sourceLocal = relative(sourceProjectRoot, absolute);`,
|
|
220
|
+
` if (sourceLocal === '' || (!sourceLocal.startsWith('..') && !isAbsolute(sourceLocal))) return resolve(runtimeProjectRoot, sourceLocal);`,
|
|
221
|
+
` }`,
|
|
222
|
+
` throw new Error('Supercov refuses a Playwright output/cwd outside the isolated project: ' + absolute);`,
|
|
223
|
+
`};`,
|
|
224
|
+
`const normalizeWebServer = server => server ? ({ ...server, cwd: runtimePath(server.cwd ?? originalDirectory) }) : server;`,
|
|
225
|
+
`const normalized = { ...resolved,`,
|
|
226
|
+
` testDir: runtimePath(resolved?.testDir),`,
|
|
227
|
+
` outputDir: runtimePath(resolved?.outputDir),`,
|
|
228
|
+
` snapshotDir: runtimePath(resolved?.snapshotDir),`,
|
|
229
|
+
` projects: resolved?.projects?.map(project => ({ ...project, testDir: runtimePath(project.testDir), outputDir: runtimePath(project.outputDir), snapshotDir: runtimePath(project.snapshotDir) })),`,
|
|
230
|
+
` webServer: Array.isArray(resolved?.webServer) ? resolved.webServer.map(normalizeWebServer) : normalizeWebServer(resolved?.webServer),`,
|
|
231
|
+
`};`,
|
|
232
|
+
`const configuredReporters = normalized.reporter;`,
|
|
233
|
+
`const reporters = configuredReporters`,
|
|
234
|
+
` ? (typeof configuredReporters === 'string' ? [[configuredReporters]] : (Array.isArray(configuredReporters[0]) ? configuredReporters : [configuredReporters]))`,
|
|
235
|
+
` : [['list']];`,
|
|
236
|
+
`const coverageReporter = resolve(runtimeProjectRoot, '.supercov/playwrightReporter.js');`,
|
|
237
|
+
`export default { ...normalized, reporter: [...reporters, [coverageReporter]] };`,
|
|
238
|
+
"",
|
|
239
|
+
].join("\n"));
|
|
240
|
+
}
|
|
241
|
+
atomicWriteFileSync(generatedViteConfig, [
|
|
242
|
+
`import { loadConfigFromFile, mergeConfig } from 'vite';`,
|
|
243
|
+
`import { isAbsolute, relative, resolve } from 'node:path';`,
|
|
244
|
+
`import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.js")).href}';`,
|
|
245
|
+
`export default async function supercovViteConfig(env) {`,
|
|
246
|
+
` const loaded = await loadConfigFromFile(env, undefined, process.cwd());`,
|
|
247
|
+
` const originalRoot = ${JSON.stringify(root)};`,
|
|
248
|
+
` const isolatedRoot = ${JSON.stringify(isolatedRoot)};`,
|
|
249
|
+
` const relocate = (value, label) => {`,
|
|
250
|
+
` const absolute = isAbsolute(value) ? value : resolve(isolatedRoot, value);`,
|
|
251
|
+
` const alreadyIsolated = relative(isolatedRoot, absolute);`,
|
|
252
|
+
` if (alreadyIsolated === '' || (!alreadyIsolated.startsWith('..') && !isAbsolute(alreadyIsolated))) return absolute;`,
|
|
253
|
+
` const local = relative(originalRoot, absolute);`,
|
|
254
|
+
` if (local.startsWith('..') || isAbsolute(local)) throw new Error('Supercov refuses ' + label + ' outside the isolated project: ' + absolute);`,
|
|
255
|
+
` return resolve(isolatedRoot, local);`,
|
|
256
|
+
` };`,
|
|
257
|
+
` const config = loaded?.config ?? {};`,
|
|
258
|
+
` 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;`,
|
|
259
|
+
` const rollupOutput = config.build?.rollupOptions?.output;`,
|
|
260
|
+
` 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) } } };`,
|
|
261
|
+
` return mergeConfig(safe, { plugins: [mcdcVitePlugin(${JSON.stringify({ root: isolatedRoot, sourceRoots: project.sourceRoots, manifestPath })})] });`,
|
|
262
|
+
`}`,
|
|
263
|
+
"",
|
|
264
|
+
].join("\n"));
|
|
265
|
+
atomicWriteFileSync(generatedVitestConfig, [
|
|
266
|
+
`import { loadConfigFromFile, mergeConfig } from 'vite';`,
|
|
267
|
+
`import { resolve } from 'node:path';`,
|
|
268
|
+
`import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.js")).href}';`,
|
|
269
|
+
`import SupercovVitestReporter from '${pathToFileURL(resolve(packageSource, "vitestReporter.js")).href}';`,
|
|
270
|
+
`const discoveredConfig = ${JSON.stringify(isolatedVitestConfig)};`,
|
|
271
|
+
`export default async function supercovVitestConfig(env) {`,
|
|
272
|
+
` const originalPath = process.env.SUPERCOV_ORIGINAL_VITEST_CONFIG || discoveredConfig;`,
|
|
273
|
+
` const loaded = originalPath ? await loadConfigFromFile(env, originalPath, process.cwd()) : undefined;`,
|
|
274
|
+
` const config = mergeConfig(loaded?.config ?? {}, {`,
|
|
275
|
+
` cacheDir: resolve(process.cwd(), '.supercov/vitest-cache'),`,
|
|
276
|
+
` plugins: ${project.buildAdapter === "vite" ? `[mcdcVitePlugin(${JSON.stringify({ root: isolatedRoot, sourceRoots: project.sourceRoots, manifestPath })})]` : "[]"},`,
|
|
277
|
+
` test: { setupFiles: [${JSON.stringify(resolve(packageSource, "vitest.js"))}], maxConcurrency: 1 },`,
|
|
278
|
+
` });`,
|
|
279
|
+
` const configuredReporters = loaded?.config?.test?.reporters;`,
|
|
280
|
+
` config.test ??= {};`,
|
|
281
|
+
` config.test.reporters = configuredReporters`,
|
|
282
|
+
` ? [...(Array.isArray(configuredReporters) ? configuredReporters : [configuredReporters]), new SupercovVitestReporter()]`,
|
|
283
|
+
` : ['default', new SupercovVitestReporter()];`,
|
|
284
|
+
` return config;`,
|
|
285
|
+
`}`,
|
|
286
|
+
"",
|
|
287
|
+
].join("\n"));
|
|
288
|
+
const coverageEnv = {
|
|
289
|
+
...process.env,
|
|
290
|
+
SUPERCOV_EVIDENCE_DIR: evidenceDirectoryRelative,
|
|
291
|
+
SUPERCOV_EXECUTION_FINGERPRINT: runIntegrity.fingerprint.execution,
|
|
292
|
+
SUPERCOV_EXECUTION_LOG: resolve(isolatedEvidenceDirectory, "execution.jsonl"),
|
|
293
|
+
SUPERCOV_RUN_ID: runId,
|
|
294
|
+
SUPERCOV_MANIFEST: manifestPath,
|
|
295
|
+
SUPERCOV_PLAYWRIGHT_MODULE: project.playwrightModule,
|
|
296
|
+
SUPERCOV_PLAYWRIGHT_TEST_EXPORT: project.playwrightTestExport,
|
|
297
|
+
SUPERCOV_PROJECT_ROOT: isolatedRoot,
|
|
298
|
+
SUPERCOV_SOURCE_PROJECT_ROOT: root,
|
|
299
|
+
...(project.buildAdapter === "direct"
|
|
300
|
+
? { SUPERCOV_DIRECT_INSTRUMENTATION: "1" }
|
|
301
|
+
: {}),
|
|
302
|
+
SUPERCOV_GENERATED_VITEST_CONFIG: generatedVitestConfig,
|
|
303
|
+
SUPERCOV_GENERATED_PLAYWRIGHT_CONFIG: generatedPlaywrightConfig,
|
|
304
|
+
...(isolatedPlaywrightConfig
|
|
305
|
+
? { SUPERCOV_ORIGINAL_PLAYWRIGHT_CONFIG: isolatedPlaywrightConfig }
|
|
306
|
+
: {}),
|
|
307
|
+
};
|
|
308
|
+
const testNodeOptions = [
|
|
309
|
+
process.env["NODE_OPTIONS"],
|
|
310
|
+
`--import=${pathToFileURL(resolve(generatedDirectory, "register.mjs")).href}`,
|
|
311
|
+
]
|
|
312
|
+
.filter(Boolean)
|
|
313
|
+
.join(" ");
|
|
314
|
+
updateRunState(root, runId, { status: "building" });
|
|
315
|
+
console.error(`[supercov] instrumenting isolated workspace ${isolatedRoot}`);
|
|
316
|
+
if (Object.keys(project.buildEnvironment).length > 0)
|
|
317
|
+
console.error(`[supercov] inferred build mode from command/config: ${Object.entries(project.buildEnvironment)
|
|
318
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
319
|
+
.join(" ")}`);
|
|
320
|
+
if (project.buildAdapter === "direct") {
|
|
321
|
+
instrumentDirectWorkspace(isolatedRoot, project.sourceRoots, manifestPath);
|
|
322
|
+
buildResult = { status: 0, signal: null };
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
buildResult = await runChild(project.buildCommand[0], [
|
|
326
|
+
...project.buildCommand.slice(1),
|
|
327
|
+
"--",
|
|
328
|
+
"--config",
|
|
329
|
+
".supercov/vite.config.mjs",
|
|
330
|
+
], {
|
|
331
|
+
cwd: isolatedRoot,
|
|
332
|
+
env: {
|
|
333
|
+
...coverageEnv,
|
|
334
|
+
...project.buildEnvironment,
|
|
335
|
+
NODE_ENV: "production",
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
if (receivedSignal)
|
|
340
|
+
throw new Error(`Interrupted by ${receivedSignal}`);
|
|
341
|
+
if (buildResult.error)
|
|
342
|
+
throw buildResult.error;
|
|
343
|
+
if (buildResult.status === 0) {
|
|
344
|
+
updateRunState(root, runId, { status: "testing" });
|
|
345
|
+
console.error(`[supercov] running in isolated workspace: ${command.join(" ")}`);
|
|
346
|
+
testResult = await runChild(command[0], command.slice(1), {
|
|
347
|
+
cwd: isolatedRoot,
|
|
348
|
+
env: {
|
|
349
|
+
...coverageEnv,
|
|
350
|
+
NODE_OPTIONS: testNodeOptions,
|
|
351
|
+
SUPERCOV_CJS_INTERCEPT: "1",
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
if (receivedSignal)
|
|
355
|
+
throw new Error(`Interrupted by ${receivedSignal}`);
|
|
356
|
+
if (testResult.error)
|
|
357
|
+
throw testResult.error;
|
|
358
|
+
updateRunState(root, runId, { status: "reporting" });
|
|
359
|
+
rmSync(persistedEvidenceDirectory, { recursive: true, force: true });
|
|
360
|
+
if (existsSync(isolatedEvidenceDirectory))
|
|
361
|
+
atomicRenameSync(isolatedEvidenceDirectory, persistedEvidenceDirectory);
|
|
362
|
+
try {
|
|
363
|
+
rmSync(reportStagingDirectory, { recursive: true, force: true });
|
|
364
|
+
writeMcdcReport(persistedEvidenceDirectory, runId, runStartedAt, manifestPath, testResult.status, runIntegrity, {
|
|
365
|
+
directory: reportStagingDirectory,
|
|
366
|
+
displayDirectory: storedRunDirectory,
|
|
367
|
+
});
|
|
368
|
+
atomicWriteFileSync(resolve(reportStagingDirectory, "run.json"), `${JSON.stringify({
|
|
369
|
+
id: runId,
|
|
370
|
+
startedAt,
|
|
371
|
+
durationMs: Date.now() - runStartedAt,
|
|
372
|
+
command,
|
|
373
|
+
testExitCode: testResult.status,
|
|
374
|
+
integrity: runIntegrity,
|
|
375
|
+
isolatedBuild: true,
|
|
376
|
+
}, null, 2)}\n`);
|
|
377
|
+
atomicRenameSync(reportStagingDirectory, storedRunDirectory);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
reportFailed = true;
|
|
381
|
+
console.error("[supercov] failed to generate report", error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const resultCode = exitCode(buildResult) || exitCode(testResult) || (reportFailed ? 1 : 0);
|
|
385
|
+
updateRunState(root, runId, { status: resultCode === 0 ? "complete" : "failed" });
|
|
386
|
+
return resultCode;
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
const status = receivedSignal ? "interrupted" : "failed";
|
|
390
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
391
|
+
try {
|
|
392
|
+
updateRunState(root, runId, {
|
|
393
|
+
status,
|
|
394
|
+
...(receivedSignal ? { signal: receivedSignal } : {}),
|
|
395
|
+
error: message,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// The original error remains authoritative.
|
|
400
|
+
}
|
|
401
|
+
if (!receivedSignal)
|
|
402
|
+
console.error(`[supercov] ${message}`);
|
|
403
|
+
return receivedSignal ? signalExitCode(receivedSignal) : 1;
|
|
404
|
+
}
|
|
405
|
+
finally {
|
|
406
|
+
if (signalEscalation) {
|
|
407
|
+
clearTimeout(signalEscalation);
|
|
408
|
+
signalEscalation = undefined;
|
|
409
|
+
}
|
|
410
|
+
for (const [signal, handler] of signalHandlers)
|
|
411
|
+
process.removeListener(signal, handler);
|
|
412
|
+
try {
|
|
413
|
+
rmSync(reportStagingDirectory, { recursive: true, force: true });
|
|
414
|
+
// The stable isolated namespace is a deliberate build/snapshot cache.
|
|
415
|
+
// It never overlaps the user's ordinary build and `supercov clean`
|
|
416
|
+
// removes it deterministically.
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
console.error(`[supercov] isolated workspace cleanup failed: ${String(error)}`);
|
|
420
|
+
}
|
|
421
|
+
lock.release();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
10
424
|
const commandArgs = process.argv.slice(2);
|
|
11
425
|
const rawArgs = commandArgs[0] === "--help" || commandArgs[0] === "-h"
|
|
12
426
|
? ["help", ...commandArgs.slice(1)]
|
|
13
427
|
: commandArgs;
|
|
14
428
|
const queryCommand = rawArgs[0];
|
|
15
|
-
if (queryCommand
|
|
429
|
+
if (queryCommand === "clean") {
|
|
430
|
+
try {
|
|
431
|
+
cleanCommand(rawArgs.slice(1));
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
console.error(`[supercov] ${error instanceof Error ? error.message : String(error)}`);
|
|
435
|
+
process.exitCode = 2;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
else if (queryCommand && coverageQueryCommands.has(queryCommand)) {
|
|
16
439
|
try {
|
|
17
440
|
await runQueryCommand(queryCommand, rawArgs.slice(1));
|
|
18
441
|
}
|
|
@@ -30,216 +453,10 @@ else {
|
|
|
30
453
|
const command = separator >= 0 ? process.argv.slice(separator + 1) : [];
|
|
31
454
|
if (command.length === 0) {
|
|
32
455
|
console.error("Usage: supercov -- <test command>");
|
|
33
|
-
process.
|
|
34
|
-
}
|
|
35
|
-
const root = process.cwd();
|
|
36
|
-
const project = discoverCoverageProject(root);
|
|
37
|
-
const runId = new Date().toISOString().replace(/[:.]/g, "-");
|
|
38
|
-
const runStartedAt = Date.now();
|
|
39
|
-
const generatedDirectory = resolve(root, ".supercov");
|
|
40
|
-
const evidenceDirectoryRelative = `.supercov/evidence/${runId}`;
|
|
41
|
-
const evidenceDirectory = resolve(root, evidenceDirectoryRelative);
|
|
42
|
-
const generatedPlaywrightConfig = resolve(generatedDirectory, "playwright.config.mjs");
|
|
43
|
-
const generatedViteConfig = resolve(generatedDirectory, "vite.config.mjs");
|
|
44
|
-
const generatedVitestConfig = resolve(generatedDirectory, "vitest.config.mjs");
|
|
45
|
-
const manifestRelative = `.supercov/work/${runId}/mcdc-manifest.json`;
|
|
46
|
-
const manifestPath = resolve(root, manifestRelative);
|
|
47
|
-
const packageSource = fileURLToPath(new URL(".", import.meta.url));
|
|
48
|
-
const runIntegrity = createRunIntegrity(root, project, packageSource);
|
|
49
|
-
mkdirSync(generatedDirectory, { recursive: true });
|
|
50
|
-
mkdirSync(evidenceDirectory, { recursive: true });
|
|
51
|
-
for (const file of [
|
|
52
|
-
"playwright.js",
|
|
53
|
-
"playwrightReporter.js",
|
|
54
|
-
"provenance.js",
|
|
55
|
-
"register.mjs",
|
|
56
|
-
"resolve-loader.mjs",
|
|
57
|
-
"transport.js",
|
|
58
|
-
"types.js",
|
|
59
|
-
]) {
|
|
60
|
-
copyFileSync(resolve(packageSource, file), resolve(generatedDirectory, file));
|
|
456
|
+
process.exitCode = 2;
|
|
61
457
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
writeFileSync(generatedPlaywrightAdapter, readFileSync(generatedPlaywrightAdapter, "utf8")
|
|
65
|
-
.replace("__SUPERCOV_EVIDENCE_DIRECTORY__", evidenceDirectoryRelative)
|
|
66
|
-
.replace("__SUPERCOV_PLAYWRIGHT_MODULE__", project.playwrightModule)
|
|
67
|
-
.replace("__SUPERCOV_RUN_ID__", runId));
|
|
68
|
-
writeFileSync(generatedPlaywrightReporter, readFileSync(generatedPlaywrightReporter, "utf8").replace("__SUPERCOV_EVIDENCE_DIRECTORY__", evidenceDirectoryRelative));
|
|
69
|
-
const generatedResolveLoader = resolve(generatedDirectory, "resolve-loader.mjs");
|
|
70
|
-
writeFileSync(generatedResolveLoader, readFileSync(generatedResolveLoader, "utf8").replace("__SUPERCOV_PLAYWRIGHT_MODULE__", project.playwrightModule));
|
|
71
|
-
if (project.playwrightConfig) {
|
|
72
|
-
const configImport = project.essentialOffline
|
|
73
|
-
? `../${relative(root, project.playwrightConfig).split(sep).join("/")}`
|
|
74
|
-
: pathToFileURL(project.playwrightConfig).href;
|
|
75
|
-
writeFileSync(generatedPlaywrightConfig, [
|
|
76
|
-
`import './register.mjs';`,
|
|
77
|
-
`import { isAbsolute, resolve } from 'node:path';`,
|
|
78
|
-
`import original from '${configImport}';`,
|
|
79
|
-
`const resolved = typeof original === 'function' ? await original({ command: 'test', mode: 'test' }) : original;`,
|
|
80
|
-
`const originalDirectory = ${JSON.stringify(dirname(project.playwrightConfig))};`,
|
|
81
|
-
`const absoluteFromOriginal = value => value && !isAbsolute(value) ? resolve(originalDirectory, value) : value;`,
|
|
82
|
-
`const normalizeWebServer = server => server ? ({ ...server, cwd: absoluteFromOriginal(server.cwd ?? originalDirectory) }) : server;`,
|
|
83
|
-
`const normalized = { ...resolved,`,
|
|
84
|
-
` testDir: absoluteFromOriginal(resolved?.testDir),`,
|
|
85
|
-
` outputDir: absoluteFromOriginal(resolved?.outputDir),`,
|
|
86
|
-
` snapshotDir: absoluteFromOriginal(resolved?.snapshotDir),`,
|
|
87
|
-
` projects: resolved?.projects?.map(project => ({ ...project, testDir: absoluteFromOriginal(project.testDir), outputDir: absoluteFromOriginal(project.outputDir), snapshotDir: absoluteFromOriginal(project.snapshotDir) })),`,
|
|
88
|
-
` webServer: Array.isArray(resolved?.webServer) ? resolved.webServer.map(normalizeWebServer) : normalizeWebServer(resolved?.webServer),`,
|
|
89
|
-
`};`,
|
|
90
|
-
`const configuredReporters = normalized.reporter;`,
|
|
91
|
-
`const reporters = configuredReporters`,
|
|
92
|
-
` ? (typeof configuredReporters === 'string' ? [[configuredReporters]] : (Array.isArray(configuredReporters[0]) ? configuredReporters : [configuredReporters]))`,
|
|
93
|
-
` : [['list']];`,
|
|
94
|
-
`const coverageReporter = process.env.TEST_IN_CONTAINER === 'true'`,
|
|
95
|
-
` ? '/workspace/.supercov/playwrightReporter.js'`,
|
|
96
|
-
` : ${JSON.stringify(resolve(packageSource, "playwrightReporter.js"))};`,
|
|
97
|
-
`export default { ...normalized, reporter: [...reporters, [coverageReporter]] };`,
|
|
98
|
-
"",
|
|
99
|
-
].join("\n"));
|
|
458
|
+
else {
|
|
459
|
+
process.exitCode = await createCoverageRun(command);
|
|
100
460
|
}
|
|
101
|
-
writeFileSync(generatedViteConfig, [
|
|
102
|
-
`import { loadConfigFromFile, mergeConfig } from 'vite';`,
|
|
103
|
-
`import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.js")).href}';`,
|
|
104
|
-
`export default async function supercovViteConfig(env) {`,
|
|
105
|
-
` const loaded = await loadConfigFromFile(env, undefined, process.cwd());`,
|
|
106
|
-
` return mergeConfig(loaded?.config ?? {}, { plugins: [mcdcVitePlugin(${JSON.stringify({ root, sourceRoots: project.sourceRoots, manifestPath })})] });`,
|
|
107
|
-
`}`,
|
|
108
|
-
"",
|
|
109
|
-
].join("\n"));
|
|
110
|
-
writeFileSync(generatedVitestConfig, [
|
|
111
|
-
`import { loadConfigFromFile, mergeConfig } from 'vite';`,
|
|
112
|
-
`import { mcdcVitePlugin } from '${pathToFileURL(resolve(packageSource, "vitePlugin.js")).href}';`,
|
|
113
|
-
`import SupercovVitestReporter from '${pathToFileURL(resolve(packageSource, "vitestReporter.js")).href}';`,
|
|
114
|
-
`const discoveredConfig = ${JSON.stringify(project.vitestConfig)};`,
|
|
115
|
-
`export default async function supercovVitestConfig(env) {`,
|
|
116
|
-
` const originalPath = process.env.SUPERCOV_ORIGINAL_VITEST_CONFIG || discoveredConfig;`,
|
|
117
|
-
` const loaded = originalPath ? await loadConfigFromFile(env, originalPath, process.cwd()) : undefined;`,
|
|
118
|
-
` const config = mergeConfig(loaded?.config ?? {}, {`,
|
|
119
|
-
` plugins: [mcdcVitePlugin(${JSON.stringify({ root, sourceRoots: project.sourceRoots, manifestPath })})],`,
|
|
120
|
-
` test: { setupFiles: [${JSON.stringify(resolve(packageSource, "vitest.js"))}], maxConcurrency: 1 },`,
|
|
121
|
-
` });`,
|
|
122
|
-
` const configuredReporters = loaded?.config?.test?.reporters;`,
|
|
123
|
-
` config.test ??= {};`,
|
|
124
|
-
` config.test.reporters = configuredReporters`,
|
|
125
|
-
` ? [...(Array.isArray(configuredReporters) ? configuredReporters : [configuredReporters]), new SupercovVitestReporter()]`,
|
|
126
|
-
` : ['default', new SupercovVitestReporter()];`,
|
|
127
|
-
` return config;`,
|
|
128
|
-
`}`,
|
|
129
|
-
"",
|
|
130
|
-
].join("\n"));
|
|
131
|
-
const coverageEnv = {
|
|
132
|
-
...process.env,
|
|
133
|
-
TEST_MCDC: "true",
|
|
134
|
-
TEST_OFFLINE_RESULTS_RUN_ID: runId,
|
|
135
|
-
SUPERCOV_EVIDENCE_DIR: evidenceDirectoryRelative,
|
|
136
|
-
SUPERCOV_RUN_ID: runId,
|
|
137
|
-
SUPERCOV_MANIFEST: manifestRelative,
|
|
138
|
-
SUPERCOV_PLAYWRIGHT_MODULE: project.playwrightModule,
|
|
139
|
-
SUPERCOV_PROJECT_ROOT: root,
|
|
140
|
-
SUPERCOV_GENERATED_VITEST_CONFIG: generatedVitestConfig,
|
|
141
|
-
SUPERCOV_GENERATED_PLAYWRIGHT_CONFIG: generatedPlaywrightConfig,
|
|
142
|
-
};
|
|
143
|
-
const testNodeOptions = [
|
|
144
|
-
process.env["NODE_OPTIONS"],
|
|
145
|
-
`--import=${pathToFileURL(resolve(generatedDirectory, "register.mjs")).href}`,
|
|
146
|
-
]
|
|
147
|
-
.filter(Boolean)
|
|
148
|
-
.join(" ");
|
|
149
|
-
console.error(`[supercov] instrumenting ${root}`);
|
|
150
|
-
const instrumentedBuild = spawnSync(project.buildCommand[0], [
|
|
151
|
-
...project.buildCommand.slice(1),
|
|
152
|
-
"--",
|
|
153
|
-
"--config",
|
|
154
|
-
".supercov/vite.config.mjs",
|
|
155
|
-
], {
|
|
156
|
-
cwd: root,
|
|
157
|
-
env: {
|
|
158
|
-
...coverageEnv,
|
|
159
|
-
...(project.essentialOffline ? { TEST_OFFLINE: "true" } : {}),
|
|
160
|
-
NODE_ENV: "production",
|
|
161
|
-
},
|
|
162
|
-
stdio: "inherit",
|
|
163
|
-
});
|
|
164
|
-
let testRun;
|
|
165
|
-
let reportFailed = false;
|
|
166
|
-
if (!instrumentedBuild.error && instrumentedBuild.status === 0) {
|
|
167
|
-
// The Essential offline VM pool keys its snapshot on dependency overlays,
|
|
168
|
-
// not the mounted application build. A tiny build-hash overlay invalidates
|
|
169
|
-
// the warm snapshot exactly when the instrumented bundle changes. Other
|
|
170
|
-
// runners simply ignore these environment variables.
|
|
171
|
-
const overlayPackage = "essential-seo/.mcdc-pool";
|
|
172
|
-
if (project.essentialOffline) {
|
|
173
|
-
const overlayDist = resolve(root, ".mcdc-pool/dist");
|
|
174
|
-
const overlayMarker = resolve(overlayDist, "instrumented-build.sha256");
|
|
175
|
-
const bundleHash = createHash("sha256")
|
|
176
|
-
.update(readFileSync(resolve(root, "build/server/index.js")))
|
|
177
|
-
.update(readFileSync(manifestPath))
|
|
178
|
-
.digest("hex");
|
|
179
|
-
mkdirSync(overlayDist, { recursive: true });
|
|
180
|
-
if (!existsSync(overlayMarker) ||
|
|
181
|
-
readFileSync(overlayMarker, "utf8").trim() !== bundleHash) {
|
|
182
|
-
writeFileSync(overlayMarker, `${bundleHash}\n`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
console.error(`[supercov] running: ${command.join(" ")}`);
|
|
186
|
-
testRun = spawnSync(command[0], command.slice(1), {
|
|
187
|
-
cwd: root,
|
|
188
|
-
stdio: "inherit",
|
|
189
|
-
env: {
|
|
190
|
-
...coverageEnv,
|
|
191
|
-
NODE_OPTIONS: testNodeOptions,
|
|
192
|
-
...(!project.essentialOffline
|
|
193
|
-
? {
|
|
194
|
-
SUPERCOV_CJS_INTERCEPT: "1",
|
|
195
|
-
}
|
|
196
|
-
: {}),
|
|
197
|
-
...(project.essentialOffline
|
|
198
|
-
? {
|
|
199
|
-
TEST_OFFLINE_LOCAL_OVERLAY: "true",
|
|
200
|
-
TEST_OFFLINE_LOCAL_OVERLAY_PKGS: overlayPackage,
|
|
201
|
-
TEST_PLAYWRIGHT_CONFIG: ".supercov/playwright.config.mjs",
|
|
202
|
-
}
|
|
203
|
-
: {}),
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
try {
|
|
207
|
-
writeMcdcReport(evidenceDirectory, runId, runStartedAt, manifestPath, testRun.status, runIntegrity);
|
|
208
|
-
const storedRunDirectory = resolve(root, ".supercov/runs", runId);
|
|
209
|
-
writeFileSync(resolve(storedRunDirectory, "run.json"), `${JSON.stringify({
|
|
210
|
-
id: runId,
|
|
211
|
-
startedAt: new Date(runStartedAt).toISOString(),
|
|
212
|
-
durationMs: Date.now() - runStartedAt,
|
|
213
|
-
command,
|
|
214
|
-
testExitCode: testRun.status,
|
|
215
|
-
integrity: runIntegrity,
|
|
216
|
-
}, null, 2)}\n`);
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
reportFailed = true;
|
|
220
|
-
console.error("[supercov] failed to generate report", error);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
console.error("[supercov] restoring the ordinary build");
|
|
224
|
-
const restore = spawnSync(project.buildCommand[0], project.buildCommand.slice(1), {
|
|
225
|
-
cwd: root,
|
|
226
|
-
env: {
|
|
227
|
-
...process.env,
|
|
228
|
-
...(project.essentialOffline ? { TEST_OFFLINE: "true" } : {}),
|
|
229
|
-
NODE_ENV: "production",
|
|
230
|
-
},
|
|
231
|
-
stdio: "inherit",
|
|
232
|
-
});
|
|
233
|
-
if (instrumentedBuild.error)
|
|
234
|
-
throw instrumentedBuild.error;
|
|
235
|
-
if (testRun?.error)
|
|
236
|
-
throw testRun.error;
|
|
237
|
-
if (restore.error)
|
|
238
|
-
throw restore.error;
|
|
239
|
-
process.exitCode =
|
|
240
|
-
instrumentedBuild.status ||
|
|
241
|
-
testRun?.status ||
|
|
242
|
-
restore.status ||
|
|
243
|
-
(reportFailed ? 1 : 0);
|
|
244
461
|
}
|
|
245
462
|
//# sourceMappingURL=cli.js.map
|