81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
import { join, relative, resolve } from "node:path";
|
|
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import pkg from "../package.json" with { type: "json" };
|
|
|
|
const ROOT = resolve(__dirname, "..");
|
|
const SRC = join(ROOT, "src");
|
|
|
|
function walk(dir: string, out: string[] = []): string[] {
|
|
for (const entry of readdirSync(dir)) {
|
|
const full = join(dir, entry);
|
|
const st = statSync(full);
|
|
if (st.isDirectory()) {
|
|
walk(full, out);
|
|
} else {
|
|
out.push(full);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
describe("dep-graph", () => {
|
|
it("every impl .ts has a sibling .md", { timeout: 5000 }, () => {
|
|
const missing: string[] = [];
|
|
for (const f of walk(SRC)) {
|
|
if (!f.endsWith(".ts")) continue;
|
|
if (f.endsWith(".test.ts")) continue;
|
|
if (f.endsWith(".spec-conformance.test.ts")) continue;
|
|
if (f.endsWith(".types.ts")) continue;
|
|
if (f.endsWith("/index.ts")) continue;
|
|
const md = f.replace(/\.ts$/, ".md");
|
|
if (!existsSync(md)) missing.push(relative(ROOT, md));
|
|
}
|
|
expect(missing).toEqual([]);
|
|
});
|
|
|
|
it("every subpath export resolves to a real source file", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const entries = pkg.exports as Record<string, unknown>;
|
|
const missing: string[] = [];
|
|
for (const [key, value] of Object.entries(entries)) {
|
|
if (key === "./package.json") continue;
|
|
const distPath =
|
|
typeof value === "object" && value !== null
|
|
? (
|
|
value as {
|
|
import?: { default?: string };
|
|
}
|
|
).import?.default
|
|
: (value as string);
|
|
if (typeof distPath !== "string") continue;
|
|
const srcPath = distPath
|
|
.replace(/^\.\/dist\//, "./src/")
|
|
.replace(/\.js$/, ".ts");
|
|
const abs = join(ROOT, srcPath);
|
|
if (!existsSync(abs)) missing.push(`${key} → ${srcPath}`);
|
|
}
|
|
expect(missing).toEqual([]);
|
|
});
|
|
|
|
it("runtime dependencies match the spec (error, tslib, zod only)", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const deps = Object.keys(pkg.dependencies ?? {}).sort();
|
|
expect(deps).toEqual(["@suaveplan/error", "tslib", "zod"]);
|
|
});
|
|
|
|
it("no test-runner imports leak into src/", { timeout: 5000 }, () => {
|
|
const offenders: string[] = [];
|
|
const re = /from\s+["'](vitest|bun:test)["']/;
|
|
for (const f of walk(SRC)) {
|
|
if (!f.endsWith(".ts")) continue;
|
|
if (f.endsWith(".test.ts")) continue;
|
|
const text = readFileSync(f, "utf8");
|
|
if (re.test(text)) offenders.push(relative(ROOT, f));
|
|
}
|
|
expect(offenders).toEqual([]);
|
|
});
|
|
});
|