import { spawn } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; const workingDirectory = await fs.realpath(process.cwd()); const vitestCliPath = path.join(workingDirectory, "node_modules", "vitest", "vitest.mjs"); const unitTestsRoot = path.join(workingDirectory, "tests", "unit"); const serialListPath = path.join(unitTestsRoot, "serial-tests.txt"); const args = parseArgs(process.argv.slice(2)); let activeChild = null; let isShuttingDown = false; for (const signal of ["SIGINT", "SIGTERM"]) { process.on(signal, () => { void shutdown(signal).finally(() => { process.exit(130); }); }); } function parseArgs(rawArgs) { const parsed = { help: false, specFilters: [], forwardedArgs: [], }; for (let index = 0; index < rawArgs.length; index += 1) { const value = rawArgs[index]; if (value === "--") { parsed.forwardedArgs.push(...rawArgs.slice(index + 1)); break; } if (value === "--help" || value === "-h") { parsed.help = true; continue; } if (value.startsWith("-")) { parsed.forwardedArgs.push(...rawArgs.slice(index)); break; } parsed.specFilters.push(value); } return parsed; } function normalizePathSegments(value) { return value.replace(/\\/g, "/"); } function printUsage() { console.log(` Vitest fast unit runner Usage: node scripts/run-vitest-unit-fast.mjs [spec-filter ...] [-- ] Runs all unit specs except entries in tests/unit/serial-tests.txt. Environment: VITEST_FAST_MAX_WORKERS Optional max workers for the fast pass Examples: npm run test:unit:fast npm run test:unit:fast -- tests/unit/workfeed npm run test:unit:fast -- --reporter=verbose `); } async function collectSpecFiles(targetDirectory) { const directoryEntries = await fs.readdir(targetDirectory, { withFileTypes: true }); const files = []; for (const entry of directoryEntries) { const absolutePath = path.join(targetDirectory, entry.name); if (entry.isDirectory()) { files.push(...(await collectSpecFiles(absolutePath))); continue; } if (entry.isFile() && entry.name.endsWith(".spec.js")) { files.push(absolutePath); } } return files.sort((left, right) => left.localeCompare(right)); } async function readSerialAllowlist() { const contents = await fs.readFile(serialListPath, "utf8"); return new Set( contents .split(/\r?\n/u) .map((line) => line.replace(/#.*/u, "").trim()) .filter(Boolean) .map(normalizePathSegments) ); } function matchesFilter(relativePath, rawFilter) { const normalizedRelativePath = normalizePathSegments(relativePath); const normalizedFilter = normalizePathSegments(rawFilter.trim()); if (!normalizedFilter) { return false; } return ( normalizedRelativePath === normalizedFilter || normalizedRelativePath.endsWith(normalizedFilter) || normalizedRelativePath.includes(normalizedFilter) ); } function applyFilters(files, specFilters) { if (specFilters.length === 0) { return files; } return files.filter((absolutePath) => { const relativePath = path.relative(workingDirectory, absolutePath); return specFilters.some((filter) => matchesFilter(relativePath, filter)); }); } function hasForwardedArg(name) { return args.forwardedArgs.some((value) => value === name || value.startsWith(`${name}=`)); } function resolveMaxWorkers() { return (process.env.VITEST_FAST_MAX_WORKERS || "").trim(); } async function runVitest(files) { const enforcedArgs = []; const configuredMaxWorkers = resolveMaxWorkers(); if (configuredMaxWorkers && !hasForwardedArg("--maxWorkers")) { enforcedArgs.push(`--maxWorkers=${configuredMaxWorkers}`); } return new Promise((resolve) => { activeChild = spawn(process.execPath, [vitestCliPath, "run", ...files, ...enforcedArgs, ...args.forwardedArgs], { cwd: workingDirectory, env: process.env, stdio: "inherit", windowsHide: true, }); activeChild.on("close", (code) => { activeChild = null; resolve(code ?? 1); }); }); } async function shutdown(signal) { if (isShuttingDown) { return; } isShuttingDown = true; console.error(`Received ${signal}. Stopping active Vitest fast run...`); if (activeChild?.pid) { if (process.platform === "win32") { spawn("taskkill", ["/PID", String(activeChild.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, }); } else { activeChild.kill("SIGTERM"); } } } async function main() { if (args.help) { printUsage(); return; } await fs.access(vitestCliPath); const serialAllowlist = await readSerialAllowlist(); const discoveredFiles = await collectSpecFiles(unitTestsRoot); const fastFiles = discoveredFiles.filter((absolutePath) => { const relativePath = normalizePathSegments(path.relative(workingDirectory, absolutePath)); return !serialAllowlist.has(relativePath); }); const selectedFiles = applyFilters(fastFiles, args.specFilters); if (selectedFiles.length === 0) { console.log("No parallel-safe unit spec files matched the provided filters."); return; } console.log( `Running ${selectedFiles.length} parallel-safe unit spec file(s); ${serialAllowlist.size} spec file(s) remain in the serial allowlist.` ); const code = await runVitest(selectedFiles); if (code !== 0) { process.exitCode = code; } } await main().catch((error) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; });