Files
tibi-docs/scripts/sync-server-docs.mjs
T

73 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
/**
* sync-server-docs.mjs — Single Source bleibt in den Quell-Repos,
* diese Doku rendert nur. Kopiert:
* - ../tibi-server/docs/*.md -> src/content/docs/server/ (ohne uebersicht.md zu überschreiben)
* - ../tibi-admin-nova/docs/*.md -> src/content/docs/nova/ (+ test-plans/)
* Fügt fehlendes Starlight-Frontmatter (title) aus Dateinamen/H1 hinzu.
*/
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const serverSrc = join(root, '..', 'tibi-server', 'docs');
const novaSrc = join(root, '..', 'tibi-admin-nova', 'docs');
const serverDst = join(root, 'src', 'content', 'docs', 'server');
const novaDst = join(root, 'src', 'content', 'docs', 'nova');
const titleFromName = (f) =>
f.replace(/\.md$/, '').replace(/^\d+-/, '').split('-')
.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
function ensureFrontmatter(file, fallbackTitle) {
let text = readFileSync(file, 'utf8');
if (text.startsWith('---')) return; // hat schon Frontmatter
const h1 = text.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? fallbackTitle;
writeFileSync(file, `---\ntitle: '${h1.replace(/'/g, "''")}'\n---\n\n${text}`);
}
function syncTree(srcDir, dstDir, keep) {
let n = 0;
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
const s = join(srcDir, entry.name);
const d = join(dstDir, entry.name);
if (entry.isDirectory()) {
mkdirSync(d, { recursive: true });
n += syncTree(s, d, []);
} else if (entry.name.endsWith('.md')) {
if (keep.includes(entry.name)) continue;
cpSync(s, d);
ensureFrontmatter(d, titleFromName(entry.name));
n++;
} else {
cpSync(s, d);
n++;
}
}
return n;
}
function syncDir(src, dst, { keep = [] } = {}) {
if (!existsSync(src)) {
console.warn(`[sync] Quelle fehlt, übersprungen: ${src}`);
return 0;
}
mkdirSync(dst, { recursive: true });
return syncTree(src, dst, keep);
}
// Alte Sync-Artefakte aufräumen (nur generierte, eigene uebersicht/konzept bleiben via keep)
const n1 = syncDir(serverSrc, serverDst, { keep: ['uebersicht.md'] });
const n2 = syncDir(novaSrc, novaDst, { keep: ['konzept.md'] });
console.log(`[sync] ${n1} Server-Dateien, ${n2} Nova-Dateien synchronisiert.`);
// Scaffold-Reste entfernen, falls vorhanden
for (const stale of [
join(root, 'src', 'content', 'docs', 'guides'),
join(root, 'src', 'content', 'docs', 'reference', 'example.md'),
]) {
if (existsSync(stale)) rmSync(stale, { recursive: true, force: true });
}