import path from "node:path"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const scriptDir = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(scriptDir, ".."); const gitPathspecs = [":(glob)tests/**/*.js", ":(glob)tests/**/*.ts"]; function run(command, args, options = {}) { const result = spawnSync(command, args, { cwd: repoRoot, encoding: "utf8", ...options, }); if (result.error) { throw result.error; } return result; } function fail(message) { console.error(message); process.exit(1); } function runNpmScript(scriptName) { if (process.env.npm_execpath) { return run(process.execPath, [process.env.npm_execpath, "run", scriptName], { stdio: "inherit" }); } const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm"; return run(npmCommand, ["run", scriptName], { shell: process.platform === "win32", stdio: "inherit", }); } function parseLines(output) { return output .split(/\r?\n/) .map((line) => line.trim()) .filter(Boolean); } const worktreeResult = run("git", ["rev-parse", "--show-toplevel"]); if (worktreeResult.status !== 0) { fail("Pre-commit formatting must run inside the front-end-vue Git worktree."); } const gitRoot = path.resolve(worktreeResult.stdout.trim()); if (gitRoot !== repoRoot) { fail(`Expected front-end-vue to be the Git root, but git reported: ${gitRoot}`); } const unstagedTrackedResult = run("git", ["diff", "--name-only", "--", ...gitPathspecs]); if (unstagedTrackedResult.status !== 0) { fail("Unable to inspect unstaged test-file changes before running Prettier."); } const untrackedResult = run("git", ["ls-files", "--others", "--exclude-standard", "--", ...gitPathspecs]); if (untrackedResult.status !== 0) { fail("Unable to inspect untracked test files before running Prettier."); } const unsafePaths = [ ...parseLines(unstagedTrackedResult.stdout), ...parseLines(untrackedResult.stdout), ]; if (unsafePaths.length > 0) { const formattedPaths = unsafePaths.map((filePath) => ` - ${filePath}`).join("\n"); fail( [ "Pre-commit formatting aborted because tests contain unstaged changes.", "Stage or discard these files before committing so the hook does not add unrelated work:", formattedPaths, ].join("\n") ); } console.log("Running Prettier for frontend test files..."); const formatResult = runNpmScript("format:tests"); if (formatResult.status !== 0) { process.exit(formatResult.status ?? 1); } console.log("Restaging formatted frontend test files..."); const knownFilesResult = run("git", ["ls-files", "--cached", "--others", "--exclude-standard", "--", ...gitPathspecs]); if (knownFilesResult.status !== 0) { fail("Prettier completed, but the hook could not resolve test files for restaging."); } const filesToStage = parseLines(knownFilesResult.stdout); if (filesToStage.length === 0) { process.exit(0); } const addResult = run("git", ["add", "--", ...filesToStage], { stdio: "inherit" }); if (addResult.status !== 0) { fail("Prettier completed, but git add failed while restaging formatted test files."); }