import { readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; const root = join(__dirname, ".."); /** Arquivos de código (fora dos testes) de app/ e lib/ cuja primeira instrução é a diretiva "use client". */ function findClientFiles() { return ["app", "lib"] .flatMap((dir) => readdirSync(join(root, dir), { recursive: true, encoding: "utf8" }).map( (file) => `${dir}/${file.replaceAll("\\", "/")}`, ), ) .filter( (file) => /\.tsx?$/.test(file) && !/\.test\.tsx?$/.test(file) && /^\s*["']use client["']/.test(readFileSync(join(root, file), "utf8")), ) .sort(); } describe("fronteira entre servidor e cliente", () => { it.each([ "app/cases/_components/case-media.ts", "lib/og-card.tsx", "lib/portfolio-data.server.ts", ])("%s só pode ser importado no servidor", (file) => { const source = readFileSync(join(root, file), "utf8"); expect(source).toMatch(/^import "server-only";$/m); }); it("declara server-only como dependência", () => { const manifest = JSON.parse( readFileSync(join(root, "package.json"), "utf8"), ) as { dependencies: Record }; expect(manifest.dependencies["server-only"]).toBeDefined(); }); it("não deixa nenhum componente de cliente importar um módulo só de servidor", () => { const serverOnly = /from "(?:@\/lib\/og-card|@\/lib\/portfolio-data\.server|(?:\.{1,2}\/)+(?:[\w-]+\/)*case-media)"/; const clientFiles = findClientFiles(); // a busca precisa achar os componentes de cliente que existem, senão o teste passaria vazio expect(clientFiles).toEqual( expect.arrayContaining([ "app/cases/_components/CasePlayer.tsx", "app/curriculo/CvDownloadLink.tsx", ]), ); for (const file of clientFiles) { const source = readFileSync(join(root, file), "utf8"); expect(source).not.toMatch(serverOnly); } }); });