import { mkdir, readFile, readdir } from "node:fs/promises"; import { designSystemReportArtifactSchema } from "./contracts/release-artifacts.ts"; import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts"; import path from "node:path"; import { REQUIRED_COMPONENT_TOKENS, REQUIRED_PRIMITIVE_TOKENS, REQUIRED_SEMANTIC_TOKENS } from "../src/presentation/design-system/tokens/token-contract.ts"; const fixtureMode = process.argv.includes("--fixture"); const failures: string[] = []; const tokenFiles = { primitive: "src/presentation/design-system/tokens/primitive.css", semantic: "src/presentation/design-system/tokens/semantic.css", component: "src/presentation/design-system/tokens/component.css", }; const tokenLayers: Array = [ ["primitive", tokenFiles.primitive, REQUIRED_PRIMITIVE_TOKENS], ["semantic", tokenFiles.semantic, REQUIRED_SEMANTIC_TOKENS], ["component", tokenFiles.component, REQUIRED_COMPONENT_TOKENS], ]; for (const [layer, file, tokens] of tokenLayers) { const source = await readFile(file, "utf8"); for (const token of tokens) { if (!source.includes(`${token}:`)) { failures.push(`${layer} token is missing: ${token}`); } } } const cssSources = await Promise.all( [...Object.values(tokenFiles), "src/presentation/styles/theme.css"].map( async (file) => ({ file, source: await readFile(file, "utf8") }), ), ); const definitions = new Set( cssSources.flatMap(({ source }) => [...source.matchAll(/(--[a-z0-9-]+)\s*:/g)].map((match) => match[1]), ), ); for (const { file, source } of cssSources) { for (const match of source.matchAll(/var\((--[a-z0-9-]+)/g)) { if (!definitions.has(match[1])) { failures.push(`${file} uses undefined token ${match[1]}`); } } } const semanticSource = await readFile(tokenFiles.semantic, "utf8"); const darkSource = semanticSource.match(/:root\[data-theme="dark"\]\s*\{([\s\S]*?)\n\}/)?.[1] ?? ""; for (const token of [ "--color-surface", "--color-surface-muted", "--color-surface-elevated", "--color-content", "--color-content-muted", "--color-content-inverse", "--color-border", "--color-border-strong", "--color-action", "--color-action-hover", "--color-action-pressed", "--color-danger", "--color-focus", "--color-disabled-content", "--color-disabled-surface", ]) { if (!darkSource.includes(`${token}:`)) { failures.push(`dark theme token is missing: ${token}`); } } const componentSource = await readFile(tokenFiles.component, "utf8"); if (!componentSource.includes("@media (forced-colors: active)")) { failures.push("forced-colors token fallback is missing"); } async function listSourceFiles(directory: string): Promise { const result: string[] = []; for (const entry of await readdir(directory, { withFileTypes: true })) { const target = path.join(directory, entry.name); if (entry.isDirectory()) result.push(...(await listSourceFiles(target))); else if (/\.(?:ts|tsx|mts|cts)$/.test(entry.name)) result.push(target); } return result; } const sources = fixtureMode ? await listSourceFiles("tests/fixtures/design-system/forbidden") : await listSourceFiles("src"); for (const file of sources) { const source = await readFile(file, "utf8"); const vendorFacade = file === "src/presentation/design-system/icons/vendors/lucide.tsx"; if (!vendorFacade && /from\s+["']lucide-react["']/.test(source)) { failures.push(`direct icon vendor import in ${file}`); } if ( /from\s+["'](?:react-aria-components|@radix-ui\/[^"']+)["']/.test(source) ) { failures.push(`direct headless vendor import in ${file}`); } if ( !file.includes("src/presentation/design-system/") && /presentation\/design-system\/(?!index(?:\.tsx?)?["'])/.test(source) ) { failures.push(`design-system deep import in ${file}`); } if ( !file.includes("src/presentation/design-system/tokens/") && /(?:#[0-9a-f]{3,8}\b|oklch\(|rgba?\()/i.test(source) ) { failures.push(`raw palette value in ${file}`); } if ( file.includes("tests/fixtures/design-system/forbidden") && source.includes("TOOLTIP_ONLY_REQUIRED_INFORMATION") ) { failures.push(`tooltip-only required information in ${file}`); } } const report = designSystemReportArtifactSchema.parse({ schemaVersion: 1, mode: fixtureMode ? "negative-fixture" : "source", checkedTokenCount: REQUIRED_PRIMITIVE_TOKENS.length + REQUIRED_SEMANTIC_TOKENS.length + REQUIRED_COMPONENT_TOKENS.length, failures, passed: failures.length === 0, }); await mkdir("artifacts/quality", { recursive: true }); await writeValidatedJsonArtifact({ path: fixtureMode ? "artifacts/quality/design-system-fixture.json" : "artifacts/quality/design-system.json", schema: designSystemReportArtifactSchema, value: report, }); if (failures.length > 0) { process.stderr.write(`Design system contract failed:\n${failures.join("\n")}\n`); process.exit(1); } process.stdout.write( `Design system contract: ${report.checkedTokenCount} tokens and vendor boundaries PASS\n`, );