import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "@suaveplan/testing/runner"; import { build, buildRegistryIndex, buildRegistryItem, parseArgs, serialize, validateIndex, validateItem, } from "./build-registry.js"; describe("build-registry", () => { it( "buildRegistryItem produces a shadcn-compatible item", { timeout: 5000 }, () => { const item = buildRegistryItem("button"); expect(item.name).toBe("@suaveplan/button"); expect(item.type).toBe("registry:ui"); expect(item.files).toEqual([ { path: "button.tsx", type: "registry:ui" }, ]); expect(validateItem(item)).toBe(true); } ); it( "buildRegistryIndex includes every supplied name", { timeout: 5000 }, () => { const idx = buildRegistryIndex(["button", "card"]); expect(idx.items).toHaveLength(2); expect(idx.items[0]?.name).toBe("@suaveplan/button"); expect(validateIndex(idx)).toBe(true); } ); it( "parseArgs picks up --out and --components flags", { timeout: 5000 }, () => { const parsed = parseArgs([ "--out", "/tmp/out", "--components", "/tmp/src/components", ]); expect(parsed.outDir).toBe("/tmp/out"); expect(parsed.componentsDir).toBe("/tmp/src/components"); } ); it( "serialize emits canonical JSON with trailing newline", { timeout: 5000 }, () => { const out = serialize({ a: 1 }); expect(out).toBe('{\n "a": 1\n}\n'); } ); it( "build emits an empty index when components dir does not exist", { timeout: 10000 }, async () => { const dir = await mkdtemp(join(tmpdir(), "registry-empty-")); const componentsDir = join(dir, "src", "components"); const outDir = join(dir, "registry", "r"); const result = await build({ componentsDir, outDir }); expect(result.items).toEqual([]); const idx = JSON.parse(await readFile(result.indexPath, "utf8")); expect(idx.items).toEqual([]); } ); it( "build emits one manifest per component when present", { timeout: 10000 }, async () => { const dir = await mkdtemp(join(tmpdir(), "registry-some-")); const componentsDir = join(dir, "src", "components"); const outDir = join(dir, "registry", "r"); await mkdir(join(componentsDir, "button"), { recursive: true }); await writeFile( join(componentsDir, "button", "button.tsx"), "export const Button = () => null;" ); await mkdir(join(componentsDir, "card"), { recursive: true }); await writeFile( join(componentsDir, "card", "index.ts"), "export {};" ); const result = await build({ componentsDir, outDir }); expect(result.items.sort()).toEqual(["button", "card"]); const buttonItem = JSON.parse( await readFile(join(outDir, "button.json"), "utf8") ); expect(buttonItem.name).toBe("@suaveplan/button"); const idx = JSON.parse(await readFile(result.indexPath, "utf8")); expect(idx.items).toHaveLength(2); } ); it( "validateItem rejects malformed items", { timeout: 5000 }, () => { expect( validateItem({ $schema: "wrong", name: "x", type: "registry:ui", dependencies: [], registryDependencies: [], files: [], } as never) ).toBe(false); } ); it( "validateIndex rejects malformed indexes", { timeout: 5000 }, () => { expect( validateIndex({ $schema: "wrong", name: "x", items: [], } as never) ).toBe(false); } ); });