Files
suaveplan/scripts/lib/discover-packages.ts
2026-05-27 23:47:53 +01:00

79 lines
2.4 KiB
TypeScript

/**
* Walk `packages/<category>/<plugin>/<pkg>/` and return absolute directory
* paths for every workspace package (the level containing a `package.json`).
*
* Supports the canonical 3-level layout (`packages/<category>/<plugin>/<pkg>/`)
* and falls back to the legacy 2-level (`packages/<plugin>/<pkg>/`) or flat
* (`packages/<pkg>/`) layouts so experimental or transitional packages keep
* working.
*/
import { readdir, stat } from "node:fs/promises";
import { join } from "node:path";
import { CONFIG } from "./config.js";
export interface DiscoverOptions {
/** Override the monorepo root. Defaults to `CONFIG.paths.root`. */
root?: string;
/** Override the packages base. Defaults to `CONFIG.paths.packagesRoot`. */
packagesRoot?: string;
}
async function hasPackageJson(dir: string): Promise<boolean> {
try {
await stat(join(dir, "package.json"));
return true;
} catch {
return false;
}
}
async function listDirs(parent: string): Promise<string[]> {
const out: string[] = [];
const entries = await readdir(parent).catch((): string[] => []);
for (const name of entries) {
const path = join(parent, name);
const s = await stat(path).catch(() => null);
if (s?.isDirectory()) out.push(path);
}
return out;
}
export async function discoverPackageDirs(
options: DiscoverOptions = {}
): Promise<string[]> {
const root = options.root ?? CONFIG.paths.root;
const base = join(root, options.packagesRoot ?? CONFIG.paths.packagesRoot);
const dirs: string[] = [];
const level1 = await listDirs(base);
for (const lvl1 of level1) {
// Flat: packages/<pkg>/package.json
if (await hasPackageJson(lvl1)) {
dirs.push(lvl1);
continue;
}
const level2 = await listDirs(lvl1);
for (const lvl2 of level2) {
// Legacy 2-level: packages/<plugin>/<pkg>/package.json
if (await hasPackageJson(lvl2)) {
dirs.push(lvl2);
continue;
}
// Canonical 3-level: packages/<category>/<plugin>/<pkg>/package.json
const level3 = await listDirs(lvl2);
for (const lvl3 of level3) {
if (await hasPackageJson(lvl3)) {
dirs.push(lvl3);
}
}
}
}
dirs.sort();
return dirs;
}