feat: enforce frontend quality guardrails

This commit is contained in:
2026-08-24 13:07:14 +03:00
parent 826936c6a4
commit 1fc269adec
18 changed files with 542 additions and 42 deletions
+117
View File
@@ -0,0 +1,117 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
const scriptsDirectory = dirname(fileURLToPath(import.meta.url));
const projectDirectory = dirname(scriptsDirectory);
const dependencyCruiser = join(
projectDirectory,
"node_modules/dependency-cruiser/bin/dependency-cruise.mjs",
);
const dependencyConfig = join(projectDirectory, "dependency-cruiser.cjs");
async function withFixture(files, run) {
const fixtureDirectory = await mkdtemp(join(tmpdir(), "wealth-ledger-deps-"));
try {
await Promise.all(
Object.entries(files).map(async ([relativePath, contents]) => {
const filePath = join(fixtureDirectory, relativePath);
await mkdir(dirname(filePath), { recursive: true });
await writeFile(filePath, contents);
}),
);
return await run(fixtureDirectory);
} finally {
await rm(fixtureDirectory, { recursive: true, force: true });
}
}
function cruise(fixtureDirectory) {
return spawnSync(
process.execPath,
[
dependencyCruiser,
"src",
"--config",
dependencyConfig,
"--output-type",
"err-long",
],
{ cwd: fixtureDirectory, encoding: "utf8" },
);
}
function output(result) {
return `${result.stdout}${result.stderr}`;
}
test("rejects a source import cycle", async () => {
await withFixture(
{
"tsconfig.app.json": JSON.stringify({ compilerOptions: {} }),
"src/shared/i18n/index.ts": 'export { value } from "./zod";\n',
"src/shared/i18n/zod.ts": 'export { value } from ".";\n',
},
(fixtureDirectory) => {
const result = cruise(fixtureDirectory);
assert.notEqual(result.status, 0, output(result));
assert.match(output(result), /no-circular/);
},
);
});
test("rejects a deep import into another slice", async () => {
await withFixture(
{
"tsconfig.app.json": JSON.stringify({ compilerOptions: {} }),
"src/features/auth/ui/Form.ts": 'import "../../../shared/i18n/zod";\n',
"src/shared/i18n/index.ts": "export {};\n",
"src/shared/i18n/zod.ts": "export {};\n",
},
(fixtureDirectory) => {
const result = cruise(fixtureDirectory);
assert.notEqual(result.status, 0, output(result));
assert.match(output(result), /slices-must-use-public-api/);
},
);
});
test("rejects an index.tsx import from another slice", async () => {
await withFixture(
{
"tsconfig.app.json": JSON.stringify({ compilerOptions: {} }),
"src/features/auth/ui/Form.ts": 'import "../../../shared/i18n";\n',
"src/shared/i18n/index.tsx": "export {};\n",
},
(fixtureDirectory) => {
const result = cruise(fixtureDirectory);
assert.notEqual(result.status, 0, output(result));
assert.match(output(result), /slices-must-use-public-api/);
},
);
});
test("allows imports within a slice and through another slice public API", async () => {
await withFixture(
{
"tsconfig.app.json": JSON.stringify({ compilerOptions: {} }),
"src/features/auth/model/local.ts": "export const local = true;\n",
"src/features/auth/ui/Form.ts": [
'import { local } from "../model/local";\n',
'import { value } from "../../../shared/i18n";\n',
"void local;\n",
"void value;\n",
].join(""),
"src/shared/i18n/index.ts": 'export { value } from "./zod";\n',
"src/shared/i18n/zod.ts": "export const value = true;\n",
},
(fixtureDirectory) => {
const result = cruise(fixtureDirectory);
assert.equal(result.status, 0, output(result));
},
);
});