chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
import {
|
||||
access,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
readFile,
|
||||
rm,
|
||||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
import { isProductionModulePath } from "./lib/risk-coverage.ts";
|
||||
import {
|
||||
filesBelow,
|
||||
prepareRemovalFixture,
|
||||
pruneRemovalFixtureCiContract,
|
||||
pruneScriptOrchestration,
|
||||
regenerateRemovalFixtureWorkflow,
|
||||
requireRemovalFixtureEnvironment,
|
||||
runRemovalFixturePnpm,
|
||||
} from "./lib/removal-fixture.ts";
|
||||
|
||||
const fixtureParent = path.resolve(".tmp");
|
||||
await mkdir(fixtureParent, { recursive: true });
|
||||
const fixtureRoot = await mkdtemp(
|
||||
path.join(fixtureParent, "reference-feature-removal-"),
|
||||
);
|
||||
const pnpmCli = requireRemovalFixtureEnvironment("npm_execpath");
|
||||
const featureSource = "src/features/reference-feature";
|
||||
const featureTests = "tests/features/reference-feature";
|
||||
const commonTestPaths = [
|
||||
"tests/unit/external-contract-runtime.test.ts",
|
||||
"tests/unit/http-execution-v3.test.ts",
|
||||
"tests/unit/runtime-adapters.test.ts",
|
||||
];
|
||||
const featureOwnedPaths = [
|
||||
featureSource,
|
||||
featureTests,
|
||||
"tests/integration/http-scenario-catalog.test.ts",
|
||||
"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",
|
||||
"schemas",
|
||||
"config",
|
||||
"public",
|
||||
".gitea",
|
||||
".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",
|
||||
".nvmrc",
|
||||
];
|
||||
|
||||
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 INVALIDATION_REGISTRY = Object.freeze({
|
||||
topics: Object.freeze([]),
|
||||
namespaces: Object.freeze([]),
|
||||
edges: Object.freeze([]),
|
||||
});
|
||||
export const INVALIDATION_TOPIC_VERSIONS = 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<Record<string, RouteDefinition>>;
|
||||
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 = `export function createInstalledFeatureInputs(_context: unknown) {
|
||||
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 = {
|
||||
repositoryBaseline: number;
|
||||
generatedPaths: string[];
|
||||
criticalModules: Array<{
|
||||
path?: string;
|
||||
minimum?: Record<string, number>;
|
||||
}>;
|
||||
highRiskPaths: string[];
|
||||
waivers: Array<{ path?: string }>;
|
||||
};
|
||||
|
||||
type EvidenceContribution = Readonly<{ owner?: string }>;
|
||||
type EvidencePolicy = Record<
|
||||
"scenarioCatalogs" | "sourceContracts",
|
||||
unknown
|
||||
>;
|
||||
type GovernanceConsumer = Readonly<{ path?: string }>;
|
||||
type GovernanceRegistry = Record<string, unknown> & {
|
||||
consumers?: unknown;
|
||||
consumerDirectories?: unknown;
|
||||
};
|
||||
type RemovalGovernance = { registries: GovernanceRegistry[] };
|
||||
|
||||
function runPnpm(script: string, extra: string[] = []): boolean {
|
||||
return runRemovalFixturePnpm(fixtureRoot, pnpmCli, script, extra);
|
||||
}
|
||||
|
||||
try {
|
||||
await prepareRemovalFixture(fixtureRoot, copyTargets);
|
||||
for (const excludedFixtureTest of [
|
||||
"tests/unit/ci-workflow-generation.test.ts",
|
||||
"tests/unit/__snapshots__/ci-workflow-generation.test.ts.snap",
|
||||
"tests/unit/removal-fixture.test.ts",
|
||||
"tests/unit/http-scenario-evidence.test.ts",
|
||||
]) {
|
||||
await rm(path.join(fixtureRoot, excludedFixtureTest), { force: true });
|
||||
}
|
||||
|
||||
const coveragePolicyFile = path.join(
|
||||
fixtureRoot,
|
||||
"config/testing/risk-coverage.json",
|
||||
);
|
||||
const coveragePolicy = JSON.parse(
|
||||
await readFile(coveragePolicyFile, "utf8"),
|
||||
) as CoveragePolicy;
|
||||
const generatedProductionModules = new Set(coveragePolicy.generatedPaths);
|
||||
|
||||
const removedProductionModuleCount = (
|
||||
await filesBelow(path.join(fixtureRoot, featureSource))
|
||||
)
|
||||
.map((file) => path.relative(fixtureRoot, file).split(path.sep).join("/"))
|
||||
.filter(
|
||||
(file) =>
|
||||
isProductionModulePath(file) && !generatedProductionModules.has(file),
|
||||
).length;
|
||||
|
||||
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 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;
|
||||
coveragePolicy.highRiskPaths = coveragePolicy.highRiskPaths.filter(
|
||||
(modulePath) => !modulePath.startsWith(`${featureSource}/`),
|
||||
);
|
||||
coveragePolicy.waivers = coveragePolicy.waivers.filter(
|
||||
(waiver) => !waiver.path?.startsWith(`${featureSource}/`),
|
||||
);
|
||||
coveragePolicy.generatedPaths = coveragePolicy.generatedPaths.filter(
|
||||
(modulePath) => !modulePath.startsWith(`${featureSource}/`),
|
||||
);
|
||||
coveragePolicy.repositoryBaseline -= removedProductionModuleCount;
|
||||
if (coveragePolicy.repositoryBaseline <= 0) {
|
||||
throw new Error("Reference feature removal produced an invalid coverage baseline");
|
||||
}
|
||||
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 removedCiScripts = new Set([
|
||||
"check:types:fixture:feature-input",
|
||||
"check:types:fixture:reference-operation",
|
||||
"test:http-scenario-evidence",
|
||||
"test:reference-feature",
|
||||
]);
|
||||
const fixturePackagePath = path.join(fixtureRoot, "package.json");
|
||||
const fixturePackage = JSON.parse(await readFile(fixturePackagePath, "utf8")) as {
|
||||
scripts: Record<string, string>;
|
||||
};
|
||||
pruneScriptOrchestration(fixturePackage.scripts, "test:all", removedCiScripts);
|
||||
fixturePackage.scripts["test:coverage"] = fixturePackage.scripts["test:coverage"]
|
||||
.replace(" tests/features/reference-feature", "");
|
||||
delete fixturePackage.scripts["check:http-scenario-evidence"];
|
||||
delete fixturePackage.scripts["check:http-scenario-evidence:fixture"];
|
||||
await writeFile(fixturePackagePath, `${JSON.stringify(fixturePackage, null, 2)}\n`);
|
||||
await pruneRemovalFixtureCiContract({
|
||||
root: fixtureRoot,
|
||||
removedScripts: removedCiScripts,
|
||||
removedEvidencePathFragments: [
|
||||
"reference-feature.xml",
|
||||
"http-scenario-executions",
|
||||
"http-scenario-evidence",
|
||||
],
|
||||
});
|
||||
await regenerateRemovalFixtureWorkflow(fixtureRoot);
|
||||
|
||||
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]> = [
|
||||
[
|
||||
"common-test-evidence",
|
||||
(
|
||||
await Promise.all(
|
||||
commonTestPaths.map(async (testPath) => {
|
||||
try {
|
||||
await access(path.join(fixtureRoot, testPath));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
)
|
||||
).every(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")],
|
||||
["ci-contract", runPnpm("check:ci")],
|
||||
[
|
||||
"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<Record<string, unknown>> };
|
||||
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",
|
||||
`<?xml version="1.0" encoding="UTF-8"?>\n` +
|
||||
`<testsuite name="reference-feature-removal" tests="${checks.length}" failures="${passed ? 0 : 1}">` +
|
||||
checks
|
||||
.map(
|
||||
([name, result]) =>
|
||||
`<testcase name="${name}">${result ? "" : `<failure>${[...residue, ...builtResidue].join(", ")}</failure>`}</testcase>`,
|
||||
)
|
||||
.join("") +
|
||||
`</testsuite>\n`,
|
||||
);
|
||||
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.exitCode = 1;
|
||||
} else {
|
||||
process.stdout.write(
|
||||
`Reference feature removal: PASS (${checks.length} checks, no fixture IDs)\n`,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await rm(fixtureRoot, { recursive: true, force: true });
|
||||
}
|
||||
Reference in New Issue
Block a user