import { readdir, readFile, stat } from "node:fs/promises"; import { join } from "node:path"; /** * Walk `packages////README.md` for the canonical * 3-level layout. A README is only included when its sibling `package.json` * declares an `@suaveplan/*` workspace package — this skips stale plugin- or * category-level READMEs that don't correspond to a real package. */ async function findReadmes(dir: string): Promise { const results: string[] = []; const categoryEntries = await readdir(dir, { withFileTypes: true }); for (const category of categoryEntries) { if (!category.isDirectory()) continue; if (category.name.startsWith(".")) continue; const categoryPath = join(dir, category.name); const pluginEntries = await readdir(categoryPath, { withFileTypes: true, }).catch(() => []); for (const plugin of pluginEntries) { if (!plugin.isDirectory()) continue; if (plugin.name.startsWith(".")) continue; const pluginPath = join(categoryPath, plugin.name); const pkgEntries = await readdir(pluginPath, { withFileTypes: true, }).catch(() => []); for (const pkg of pkgEntries) { if (!pkg.isDirectory()) continue; if (pkg.name.startsWith(".")) continue; const pkgPath = join(pluginPath, pkg.name); // Only count this README if there's a workspace package.json // (skips stale plugin-level READMEs left from prior layouts). const pkgJsonPath = join(pkgPath, "package.json"); try { await stat(pkgJsonPath); } catch { continue; } const readmePath = join(pkgPath, "README.md"); try { await readFile(readmePath); results.push(readmePath); } catch { // README.md doesn't exist, skip } } } } return results; } async function main() { const readmePaths = await findReadmes("./packages"); const mismatches: string[] = []; for (const path of readmePaths) { const content = await readFile(path, "utf-8"); const firstLine = content.split(/\r?\n/)[0] ?? ""; if (!/^#\s@[^\s]+\s/.test(firstLine)) { mismatches.push(path); } } if (mismatches.length === 0) { console.log("All README.md files start with '# @' on the first line."); } else { console.log( `Found ${mismatches.length} README.md file(s) without '# @' as the first line:` ); for (const path of mismatches) { console.log(` ${path}`); } process.exit(1); } } main().catch((err) => { console.error(err); process.exit(1); });