import { mkdirSync, realpathSync, writeFileSync } from 'fs'; import { dirname, join, resolve } from 'path'; import { fileURLToPath } from 'url'; import { renderApp } from '../view/app'; import { sidesOf } from '../workspace/sides'; import { endpointHistory, mapSeries } from '../workspace/series'; import { renderHub } from '../view/hub'; import { workspaceRoot } from '../workspace/registry'; import { bestKind, hubProjects } from '../workspace/hubData'; import { mapPath, readMap } from '../workspace/store'; import { tolerateClosedPipe } from './stdio'; function main(): void { tolerateClosedPipe(); const args = process.argv.slice(2); const positional = args.filter((a) => !a.startsWith('--')); // cm:guard `--out=` is the form README and docs/getting-started.md name, so it has to be read // here: ignoring it fell back to ./apiflow-maps and wrote a page tree into whatever repo was cwd. const flag = args.find((a) => a.startsWith('--out='))?.slice('--out='.length); const out = resolve(flag ?? positional[0] ?? 'apiflow-maps'); const projects = hubProjects(); // cm:guard One `now` for every page in the batch: reading it per page makes two files written a // minute apart disagree about how old the same scan is. const now = Date.now(); mkdirSync(out, { recursive: true }); const written: string[] = []; for (const project of projects) { const kind = bestKind(project); if (kind === null) continue; const map = readMap(project.id, kind); if (map === null) continue; const file = join(out, `${project.id}.html`); mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, renderApp({ map, sourcePath: mapPath(project.id, kind), live: false, kind, homeHref: './index.html', projectName: project.name, sides: sidesOf(project.id), now, series: mapSeries(project.id, kind), epHistory: endpointHistory(project.id, kind), })); written.push(file); } const index = join(out, 'index.html'); writeFileSync( index, renderHub( projects, { workspace: workspaceRoot(), live: false, linkTo: (project) => (bestKind(project) === null ? null : `./${project.id}.html`), }, now ) ); console.log('## apiflow hub'); console.log(''); console.log(`**Open in a browser**: file://${index}`); console.log(`**Projects**: ${projects.length} ยท **map pages written**: ${written.length}`); const skipped = projects.length - written.length; if (skipped > 0) console.log(`**Skipped**: ${skipped} project(s) with no map yet`); console.log(''); console.log('Static output: open with file://, no server needed. Re-run after every scan.'); } const invokedDirectly = process.argv[1] !== undefined && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)); if (invokedDirectly) main();