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/reference-feature-removal"); const pnpmCli = requireEnvironment("npm_execpath"); const featureSource = "src/features/reference-feature"; const featureTests = "tests/features/reference-feature"; const featureOwnedPaths = [ featureSource, featureTests, "tests/e2e/reference-form.spec.ts", "tests/e2e/reference-route.spec.ts", "tests/mocks", "tests/fixtures/typecheck/invalid-feature-input.ts", "tests/fixtures/typecheck/invalid-reference-operation.ts", ]; 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", "tsconfig.web-worker.json", "tsconfig.service-worker.json", "vite.service-worker.config.ts", "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", ]; const emptyContracts = `import { PLATFORM_ROUTE_RUNTIME_CONTRACT } from "../contracts/route-runtime-contract.ts"; import { PLATFORM_ROUTE_REGISTRY, type RouteDefinition } from "../contracts/routes.ts"; import { PLATFORM_SCHEMA_REGISTRY } from "../contracts/schema-registry.ts"; export const INSTALLED_FEATURE_CONTRACTS: readonly unknown[] = Object.freeze([]); export const ROUTE_REGISTRY = PLATFORM_ROUTE_REGISTRY; export const ROUTE_RUNTIME_CONTRACT = PLATFORM_ROUTE_RUNTIME_CONTRACT; export const API_OPERATIONS = Object.freeze({}); export const QUERY_REGISTRY = Object.freeze({}); export const SCHEMA_REGISTRY = PLATFORM_SCHEMA_REGISTRY; export const NAVIGATION_ROUTES = Object.freeze( Object.values(ROUTE_REGISTRY) .filter((definition) => definition.navigationOrder !== null) .sort( (left, right) => left.navigationOrder! - right.navigationOrder!, ), ); export function getRoute(routeId: string): RouteDefinition { const registry = ROUTE_REGISTRY as Readonly>; const selected = registry[routeId]; if (!selected) throw new Error(\`Unregistered route: \${routeId}\`); return selected; } export function routePath(routeId: string): string { return getRoute(routeId).path; } `; const emptyRuntimes = `import { PLATFORM_ROUTE_CODECS } from "../presentation/routes/platform-route-codecs.ts"; import { PLATFORM_ROUTE_RUNTIME } from "../presentation/routes/route-runtime.tsx"; export const ROUTE_CODECS = PLATFORM_ROUTE_CODECS; export const ROUTE_RUNTIME = PLATFORM_ROUTE_RUNTIME; `; const emptyAdapters = `type FeatureContext = Readonly<{ createHttpClient(contract: Readonly>): unknown; }>; export function createInstalledFeatureInputs(_context: FeatureContext) { void _context; return Object.freeze({}); } `; const emptyContractContributions = `import { composeContractContributions, type InstalledContractContribution, type InstalledContractPackageIdentity, } from "../contracts/external-contract-runtime.ts"; export const INSTALLED_CONTRACT_CONTRIBUTIONS: readonly InstalledContractContribution[] = Object.freeze([]); export const COMPOSED_CONTRACT_CONTRIBUTIONS = composeContractContributions( INSTALLED_CONTRACT_CONTRIBUTIONS, ); export const EXPECTED_CONTRACT_SET_PACKAGES: readonly InstalledContractPackageIdentity[] = COMPOSED_CONTRACT_CONTRIBUTIONS.externalPackages; `; const emptyMessages = `export const INSTALLED_MESSAGE_CATALOGS = Object.freeze({ "ko-KR": Object.freeze({}), "en-US": Object.freeze({}), }); `; type CoveragePolicy = { criticalModules: Array<{ path?: string }>; }; type EvidenceContribution = Readonly<{ owner?: string }>; type EvidencePolicy = Record< "scenarioCatalogs" | "sourceContracts", unknown >; type GovernanceConsumer = Readonly<{ path?: string }>; type GovernanceRegistry = Record & { consumers?: unknown; consumerDirectories?: unknown; }; type RemovalGovernance = { registries: GovernanceRegistry[] }; function requireEnvironment(name: string): string { const value = process.env[name]; if (!value) throw new Error(`${name} is required for sample removal`); return value; } 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(); } function runPnpm(script: string, extra: string[] = []): boolean { const result = spawnSync(process.execPath, [pnpmCli, script, ...extra], { cwd: fixtureRoot, stdio: "inherit", }); return result.status === 0; } 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"); for (const ownedPath of featureOwnedPaths) { await rm(path.join(fixtureRoot, ownedPath), { recursive: true, force: true, }); } await writeFile( path.join(fixtureRoot, "src/features/installed-feature-contracts.ts"), emptyContracts, ); await writeFile( path.join(fixtureRoot, "src/features/installed-feature-runtimes.tsx"), emptyRuntimes, ); await writeFile( path.join(fixtureRoot, "src/features/installed-feature-adapters.ts"), emptyAdapters, ); await writeFile( path.join(fixtureRoot, "src/features/installed-feature-messages.ts"), emptyMessages, ); await writeFile( path.join(fixtureRoot, "src/features/installed-contract-contributions.ts"), emptyContractContributions, ); const vitestConfigFile = path.join(fixtureRoot, "vitest.config.ts"); const vitestConfig = await readFile(vitestConfigFile, "utf8"); const featureCoverageInclude = ` "${featureSource}/adapters/reference-http-gateway.ts",\n`; if (!vitestConfig.includes(featureCoverageInclude)) { throw new Error("Reference feature coverage include is not registered"); } await writeFile( vitestConfigFile, vitestConfig.replace(featureCoverageInclude, ""), ); const coveragePolicyFile = path.join( fixtureRoot, "config/testing/risk-coverage.json", ); const coveragePolicy = JSON.parse( await readFile(coveragePolicyFile, "utf8"), ) as CoveragePolicy; const retainedCriticalModules = coveragePolicy.criticalModules.filter( (modulePolicy) => !modulePolicy.path?.startsWith(`${featureSource}/`), ); if ( retainedCriticalModules.length === coveragePolicy.criticalModules.length ) { throw new Error("Reference feature coverage policy is not registered"); } coveragePolicy.criticalModules = retainedCriticalModules; await writeFile( coveragePolicyFile, `${JSON.stringify(coveragePolicy, null, 2)}\n`, ); const evidencePolicyFile = path.join( fixtureRoot, "config/testing/test-evidence.json", ); const evidencePolicy = JSON.parse( await readFile(evidencePolicyFile, "utf8"), ) as EvidencePolicy; let removedEvidenceContributions = 0; for (const policyKey of ["scenarioCatalogs", "sourceContracts"] as const) { const contributions = evidencePolicy[policyKey]; if (!Array.isArray(contributions)) { throw new Error(`Test evidence policy is missing ${policyKey}`); } evidencePolicy[policyKey] = contributions.filter((candidate: unknown) => { const contribution = candidate as EvidenceContribution; const retained = contribution.owner !== "reference-feature"; if (!retained) removedEvidenceContributions += 1; return retained; }); } if (removedEvidenceContributions === 0) { throw new Error("Reference feature test evidence policy is not registered"); } await writeFile( evidencePolicyFile, `${JSON.stringify(evidencePolicy, null, 2)}\n`, ); const governanceFile = path.join( fixtureRoot, "config/contracts/registry-governance.json", ); const removalGovernance = JSON.parse( await readFile(governanceFile, "utf8"), ) as RemovalGovernance; removalGovernance.registries = removalGovernance.registries.map( (registry) => ({ ...registry, ...(Array.isArray(registry.consumers) ? { consumers: registry.consumers.filter( (candidate: unknown) => { const consumer = candidate as GovernanceConsumer; return !consumer.path?.includes("features/reference-feature"); }, ), } : {}), ...(Array.isArray(registry.consumerDirectories) ? { consumerDirectories: registry.consumerDirectories.filter( (directory: unknown) => typeof directory !== "string" || !directory.includes("features/reference-feature"), ), } : {}), }), ); await writeFile( governanceFile, `${JSON.stringify(removalGovernance, null, 2)}\n`, ); const residue: string[] = []; for (const root of ["src", "tests"]) { for (const file of await filesBelow(path.join(fixtureRoot, root))) { const relative = path.relative(fixtureRoot, file); const content = await readFile(file, "utf8"); if ( /REFERENCE_RESOURCE|reference-feature|reference-resource/i.test( `${relative}\n${content}`, ) ) { residue.push(relative); } } } const checks: Array<[string, boolean]> = [ ["typecheck", runPnpm("check:types")], ["architecture", runPnpm("check:architecture")], ["registry-structure", runPnpm("check:registries:structure")], ["unit-integration", runPnpm("test:all")], ["coverage", runPnpm("test:coverage")], ["test-evidence-source", runPnpm("check:test-evidence:source")], [ "home-smoke", runPnpm("exec", [ "vitest", "run", "tests/component/router.test.tsx", "--reporter=default", ]), ], ["build", runPnpm("build")], ]; const builtResidue: 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 ( /REFERENCE_RESOURCE|reference-feature|reference-resource/i.test(content) ) { builtResidue.push(path.relative(fixtureRoot, file)); } } const routeCatalog = await import( `${new URL( "../src/features/installed-feature-contracts.ts", `file://${fixtureRoot}/scripts/`, ).href}?removed=${Date.now()}` ) as { ROUTE_REGISTRY: Readonly> }; const routeIds = Object.keys(routeCatalog.ROUTE_REGISTRY); const routeAbsent = routeIds.every((routeId) => !routeId.startsWith("REFERENCE_")); checks.push(["route-absent", routeAbsent]); checks.push(["fixture-id-residue", residue.length === 0]); checks.push(["built-fixture-id-residue", builtResidue.length === 0]); const passed = checks.every(([, result]) => result); await mkdir("artifacts/tests", { recursive: true }); await writeFile( "artifacts/tests/sample-removal.xml", `\n` + `` + checks .map( ([name, result]) => `${result ? "" : `${[...residue, ...builtResidue].join(", ")}`}`, ) .join("") + `\n`, ); await rm(fixtureRoot, { recursive: true, force: true }); if (!passed) { const failures = checks .filter(([, result]) => !result) .map(([name]) => name); process.stderr.write( `Reference feature removal failed: ${failures.join(", ")}; residue: ${[...residue, ...builtResidue].join(", ")}\n`, ); process.exit(1); } process.stdout.write( `Reference feature removal: PASS (${checks.length} checks, no fixture IDs)\n`, );