projectInfo
The project a codebase is: named for the package at its top namespace (or the namespace; with several, the codebase's directory), described by that package's doc or the first sentence of its README. Manifests are read from their source, not evaluated.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { inLibrary, librariesOf } from "#codebase/tcmts/cli/libraries"; import { absolute, isManifestPath, nsOf } from "#codebase/tcmts/cli/manifests"; import { manifestFields } from "#codebase/tcmts/share"; import type { PackageInfo, ProjectInfo } from "#codebase/tcmts/share"; import type { NameRow } from "#codebase/tcmts/store"; export function projectInfo(cb: Codebase, rows: NameRow[]): ProjectInfo { const tops = [...new Set(rows.map((r) => r.path.split(".")[0]!))]; const root = tops.length === 1 && rows.some((r) => r.path.includes(".")) ? tops[0]! : ""; const packages: PackageInfo[] = []; // (a library's packages are its own) const libraries = librariesOf(rows, (h) => cb.store.getDef(h)?.sourceText ?? ""); for (const r of rows) { if (!isManifestPath(r.path) || r.kind === "type" || inLibrary(libraries, r.path)) continue; const rec = cb.store.getDef(r.hash); const m = manifestFields(rec?.sourceText ?? ""); const name = typeof m.name === "string" ? m.name : undefined; if (!name) continue; const ns = nsOf(r.path); const strings = (v: unknown) => Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : []; const record = <T>(v: unknown, f: (x: unknown) => T | undefined) => Object.fromEntries( Object.entries(v && typeof v === "object" && !Array.isArray(v) ? v : {}).flatMap( ([k, x]) => { const y = f(x); return y === undefined ? [] : [[k, y]]; }, ), ) as Record<string, T>; const whole = (x: unknown) => (typeof x === "string" ? absolute(ns, x) : undefined); const bin = record(m.bin, whole); const description = (typeof m.description === "string" ? m.description : undefined) ?? rec?.doc ?.replace(/^\/\*\*|\*\/$/g, "") .replace(/^\s*\* ?/gm, "") .trim() .split("\n")[0]; packages.push({ name, namespace: ns, manifestPath: r.path, bins: Object.keys(bin), bin, exports: record(m.exports, (x) => strings(x).map((t) => absolute(ns, t))), apps: record(m.apps, whole), suites: record(m.suites, (x) => strings(x).map((t) => absolute(ns, t))), files: strings(m.files), with: strings(m.with), ...(typeof m.packageJson === "string" ? { packageJson: absolute(ns, m.packageJson) } : {}), ...(description ? { description } : {}), }); } const file = (leaf: string) => rows.find((r) => r.path === (root ? `${root}.${leaf}` : leaf) && r.kind === "file"); const readme = file("README"); const contributing = file("CONTRIBUTING"); const own = packages.find((p) => p.namespace === root); const firstSentence = () => { const text = readme ? (cb.store.getDef(readme.hash)?.sourceText ?? "") : ""; const para = text .split(/\n\s*\n/) .map((p) => p.trim()) .find((p) => p && !p.startsWith("#") && !p.startsWith("```") && !p.startsWith("<")); const plain = (para ?? "") .replace(/\[([^\]]*)\]\([^)]*\)/g, "$1") .replace(/[*_`]/g, "") .replace(/\s+/g, " "); return (/^.*?[.!?](\s|$)/.exec(plain)?.[0] ?? plain).trim(); }; return { name: own?.name ?? (root || cb.lay.root.split(/[\\/]/).filter(Boolean).pop() || "codebase"), description: own?.description ?? firstSentence(), root, packages: packages.sort((a, b) => (a.name < b.name ? -1 : 1)), ...(readme ? { readme } : {}), ...(contributing ? { contributing } : {}), }; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.libraries.inLibrary
- tcmts.cli.libraries.librariesOf
- tcmts.cli.manifests.absolute
- tcmts.cli.manifests.isManifestPath
- tcmts.cli.manifests.nsOf
- tcmts.share.PackageInfo
- tcmts.share.ProjectInfo
- tcmts.share.manifestFields
- tcmts.store.NameRow