Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env bun
/**
* Fails when an implementation+test pair lives flat at a package's `src/`
* root instead of inside a feature folder.
*
* CLAUDE.md §"Package Structure" mandates the 5-files-per-feature layout:
*
* src/<feature>/
* index.ts
* <feature>.ts
* <feature>.test.ts
* <feature>.types.ts
* <feature>.md
*
* The only flat exceptions allowed at `src/` root are:
* - `index.ts` (package barrel)
* - `types/`, `utils/`, `constants/` (per the documented exemption list)
*
* Any other `<name>.ts` paired with `<name>.test.ts` directly under `src/`
* (without a `<name>/` folder) is a layout violation and fails CI.
*
* Exit codes: 0 = clean, 1 = at least one violation.
*/
import { readdirSync, statSync } from "node:fs";
import { join } from "node:path";
const PACKAGES_DIR = "packages";
const ROOT_FLAT_EXEMPT = new Set(["index.ts"]);
const ROOT_DIR_EXEMPT = new Set(["types", "utils", "constants"]);
interface Violation {
readonly pkg: string;
readonly src: string;
readonly feature: string;
readonly files: readonly string[];
}
function listPackages(): readonly string[] {
const out: string[] = [];
for (const cat of readdirSync(PACKAGES_DIR)) {
const catDir = join(PACKAGES_DIR, cat);
if (!statSync(catDir).isDirectory()) continue;
for (const plugin of readdirSync(catDir)) {
const pluginDir = join(catDir, plugin);
if (!statSync(pluginDir).isDirectory()) continue;
for (const pkg of readdirSync(pluginDir)) {
const pkgDir = join(pluginDir, pkg);
if (!statSync(pkgDir).isDirectory()) continue;
const srcDir = join(pkgDir, "src");
try {
if (statSync(srcDir).isDirectory()) out.push(pkgDir);
} catch {
// no src/ — skip
}
}
}
}
return out;
}
function checkPackage(pkgDir: string): Violation[] {
const srcDir = join(pkgDir, "src");
const entries = readdirSync(srcDir);
const featuresFound = new Map<string, string[]>();
for (const name of entries) {
const full = join(srcDir, name);
if (statSync(full).isDirectory()) continue;
if (ROOT_FLAT_EXEMPT.has(name)) continue;
const match = name.match(/^(.+?)(\.test|\.types|\.md|)\.(ts|tsx|md)$/);
if (!match) continue;
const feature = match[1] as string;
const list = featuresFound.get(feature) ?? [];
list.push(name);
featuresFound.set(feature, list);
}
const violations: Violation[] = [];
for (const [feature, files] of featuresFound) {
const hasImpl = files.some((f) => f === `${feature}.ts`);
const hasTest = files.some((f) => f === `${feature}.test.ts`);
if (hasImpl && hasTest) {
violations.push({
pkg: pkgDir,
src: srcDir,
feature,
files,
});
}
}
return violations;
}
function main(): void {
const pkgs = listPackages();
const allViolations: Violation[] = [];
for (const pkg of pkgs) {
allViolations.push(...checkPackage(pkg));
}
if (allViolations.length === 0) {
console.log(
`OK ${pkgs.length} packages: every <feature>.ts + <feature>.test.ts pair lives in a feature folder.`
);
return;
}
console.error(
`Found ${allViolations.length} layout violation${allViolations.length === 1 ? "" : "s"}:\n`
);
for (const v of allViolations) {
console.error(`FAIL ${v.pkg}`);
console.error(
` feature \`${v.feature}\` is flat at src/ root; should live in src/${v.feature}/`
);
for (const f of v.files) {
console.error(` - src/${f}`);
}
}
console.error(
`\nCLAUDE.md §"Package Structure" mandates 5-files-per-feature layout. Only ${[...ROOT_DIR_EXEMPT].map((d) => `\`${d}/\``).join(", ")} dirs and \`index.ts\` are exempt at src/ root.`
);
console.error(
"Move each violation into a folder: mkdir src/<feature>/ && git mv src/<feature>.{ts,test.ts,md} src/<feature>/, then add a src/<feature>/index.ts barrel and update sibling imports."
);
process.exit(1);
}
main();