import { readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "@suaveplan/testing/runner"; const PKG_ROOT = join(__dirname, ".."); const SRC_ROOT = join(PKG_ROOT, "src"); interface PackageJson { readonly dependencies?: Record; readonly devDependencies?: Record; readonly peerDependencies?: Record; } function walkTypescript(dir: string): string[] { const entries = readdirSync(dir); const files: string[] = []; for (const entry of entries) { const full = join(dir, entry); const st = statSync(full); if (st.isDirectory()) { files.push(...walkTypescript(full)); } else if ( entry.endsWith(".ts") && !entry.endsWith(".test.ts") && !entry.endsWith(".spec.ts") ) { files.push(full); } } return files; } describe("universal dep graph", () => { it("declares only universal dependencies", { timeout: 5000 }, () => { const pkg = JSON.parse( readFileSync(join(PKG_ROOT, "package.json"), "utf8") ) as PackageJson; const runtime = Object.keys(pkg.dependencies ?? {}).sort(); expect(runtime).toEqual( [ "@suaveplan/error", "@suaveplan/payment", "@suaveplan/payment-providers", "tslib", ].sort() ); }); it("source imports only @suaveplan/* siblings, tslib, and node-free intrinsics", { timeout: 5000, }, () => { // Match any "from 'X'" or "from \"X\"" import specifier. const importPattern = /from\s+['"]([^'"]+)['"]/g; const offenders: string[] = []; for (const file of walkTypescript(SRC_ROOT)) { const text = readFileSync(file, "utf8"); for (const match of text.matchAll(importPattern)) { const spec = match[1] ?? ""; if (spec.startsWith("node:") || spec.startsWith("bun:")) { offenders.push(`${file}: ${spec}`); continue; } if ( spec === "fs" || spec === "path" || spec === "crypto" || spec === "stream" || spec === "net" || spec === "http" || spec === "https" || spec === "react" || spec === "react-dom" ) { offenders.push(`${file}: ${spec}`); } } } expect(offenders).toEqual([]); }); });