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 = /** @type {string} */ (process.env.npm_execpath); const copyTargets = [ "src", "tests", "recipes", "scripts", "config", "public", "index.html", "package.json", "tsconfig.base.json", "tsconfig.json", "tsconfig.app.json", "tsconfig.node.json", "tsconfig.test.json", "tsconfig.recipes.json", "vite.config.js", "vitest.config.js", "playwright.config.js", "eslint.config.js", ".dependency-cruiser.cjs", ]; /** @param {string} script */ function runPnpm(script) { return ( spawnSync(process.execPath, [pnpmCli, script], { cwd: fixtureRoot, stdio: "inherit", }).status === 0 ); } /** @param {string} directory @returns {Promise} */ async function filesBelow(directory) { 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 = [ ["typecheck", runPnpm("check:types")], ["architecture", runPnpm("check:architecture")], ["test", runPnpm("test:all")], ["build", runPnpm("build")], ]; /** @type {string[]} */ const residue = []; 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`, );