import { spawnSync } from "node:child_process"; import { cp, mkdir, readFile, readdir, rm, symlink, writeFile, } from "node:fs/promises"; import path from "node:path"; const fixtureRoot = path.resolve(".tmp/optional-recipe-removal"); const pnpmCli = requireEnvironment("npm_execpath"); const copyTargets = [ "src", "tests", "recipes", "scripts", "config", "public", ".storybook", "index.html", "package.json", "tsconfig.base.json", "tsconfig.json", "tsconfig.app.json", "tsconfig.node.json", "tsconfig.test.json", "tsconfig.recipes.json", "vite.config.ts", "vitest.config.ts", "playwright.config.ts", "playwright.dev.config.ts", "playwright.storybook.config.ts", "playwright.visual.config.ts", "eslint.config.ts", ".dependency-cruiser.json", ]; function requireEnvironment(name: string): string { const value = process.env[name]; if (!value) { throw new Error(`${name} is required to run removal verification`); } return value; } function runPnpm(script: string): boolean { return ( spawnSync(process.execPath, [pnpmCli, script], { cwd: fixtureRoot, stdio: "inherit", }).status === 0 ); } async function filesBelow(directory: string): Promise { const entries = await readdir(directory, { withFileTypes: true }); const groups = await Promise.all( entries.map((entry) => { const target = path.join(directory, entry.name); return entry.isDirectory() ? filesBelow(target) : [target]; }), ); return groups.flat(); } await rm(fixtureRoot, { recursive: true, force: true }); await mkdir(fixtureRoot, { recursive: true }); for (const target of copyTargets) { await cp(target, path.join(fixtureRoot, target), { recursive: true }); } await symlink(path.resolve("node_modules"), path.join(fixtureRoot, "node_modules"), "dir"); await rm(path.join(fixtureRoot, "recipes"), { recursive: true, force: true }); await rm(path.join(fixtureRoot, "tests/recipes"), { recursive: true, force: true, }); const checks: Array<[string, boolean]> = [ ["typecheck", runPnpm("check:types")], ["architecture", runPnpm("check:architecture")], ["test", runPnpm("test:all")], ["build", runPnpm("build")], ]; const residue: string[] = []; for (const file of await filesBelow(path.join(fixtureRoot, "dist"))) { if (!/\.(?:js|css|html|json)$/.test(file)) continue; const content = await readFile(file, "utf8"); if (content.includes("frontend-optional-recipe-must-not-reach-production")) { residue.push(path.relative(fixtureRoot, file)); } } checks.push(["bundle-residue", residue.length === 0]); const passed = checks.every(([, result]) => result); await mkdir("artifacts/tests", { recursive: true }); await writeFile( "artifacts/tests/optional-recipe-removal.xml", `\n` + `` + checks .map( ([name, result]) => `${result ? "" : `${residue.join(", ")}`}`, ) .join("") + `\n`, ); await rm(fixtureRoot, { recursive: true, force: true }); if (!passed) { process.stderr.write( `Optional recipe removal failed: ${checks .filter(([, result]) => !result) .map(([name]) => name) .join(", ")}\n`, ); process.exit(1); } process.stdout.write( `Optional recipe removal: PASS (${checks.length} base checks)\n`, );