import assert from "node:assert/strict"; import { readdir, readFile } from "node:fs/promises"; import { extname, join } from "node:path"; import { test } from "node:test"; const sourceRoot = new URL("../src/", import.meta.url); async function sourceFiles(relativeDirectory) { const directory = new URL(`${relativeDirectory}/`, sourceRoot); const entries = await readdir(directory, { recursive: true, withFileTypes: true, }); return entries .filter((entry) => entry.isFile() && [".ts", ".tsx"].includes(extname(entry.name))) .map((entry) => join(entry.parentPath, entry.name)); } async function read(relativePath) { return readFile(new URL(relativePath, sourceRoot), "utf8"); } test("dependency direction keeps core and reusable components below workspaces", async () => { const coreFiles = await sourceFiles("core"); const componentFiles = await sourceFiles("components"); for (const file of coreFiles) { const source = await readFile(file, "utf8"); assert.doesNotMatch( source, /from\s+["'][^"']*(?:workspaces|\/App)["']/, `${file} imports an application composition layer`, ); assert.doesNotMatch( source, /@nodedc\/ui-(?:react|dom)/, `${file} imports a visual adapter from the domain layer`, ); } for (const file of componentFiles) { const source = await readFile(file, "utf8"); assert.doesNotMatch( source, /from\s+["'][^"']*(?:workspaces|\/App)["']/, `${file} imports a workspace composition layer`, ); } }); test("shared visual primitives come only from the Design Guideline packages", async () => { const packageJson = JSON.parse( await readFile(new URL("../package.json", import.meta.url), "utf8"), ); for (const packageName of [ "@nodedc/tokens", "@nodedc/ui-core", "@nodedc/ui-react", ]) { assert.equal(typeof packageJson.dependencies[packageName], "string"); } const files = await sourceFiles("."); for (const file of files) { const source = await readFile(file, "utf8"); assert.doesNotMatch(source, /lucide-react/); assert.doesNotMatch(source, /NODEDC_DESIGN_GUIDELINE\/(?:apps|src)\//); } }); test("laboratory UI is a bounded feature slice, not a central workspace branch", async () => { const workspaceHub = await read("workspaces/Workspaces.tsx"); const laboratory = await read( "workspaces/laboratory/LaboratoryArchiveWorkspace.tsx", ); const workspaceCss = await read("styles/workspaces.css"); const laboratoryCss = await read("styles/laboratory.css"); const laboratoryReportingCss = await read("styles/laboratory-reporting.css"); const e30Review = await read("workspaces/E30ReviewWorkspace.tsx"); const e30HumanReview = await read( "workspaces/E30HumanReviewPanel.tsx", ); const e30HumanReviewCss = await read("styles/e30-human-review.css"); assert.doesNotMatch( workspaceHub, /fetchE(?:29|30)|LaboratoryWorkTemplate|function\s+E(?:29|30)LaboratoryResult/, ); assert.match( workspaceHub, / { const ratchets = [ ["App.tsx", 1_250], ["workspaces/Workspaces.tsx", 1_200], ["workspaces/laboratory/LaboratoryArchiveWorkspace.tsx", 1_000], ["styles/workspaces.css", 4_350], ["styles/laboratory.css", 900], ["styles/laboratory-reporting.css", 100], ]; for (const [relativePath, maximumLines] of ratchets) { const lineCount = (await read(relativePath)).split("\n").length; assert.ok( lineCount <= maximumLines, `${relativePath} has ${lineCount} lines; split the feature instead of raising ${maximumLines}`, ); } });