109 lines
2.9 KiB
JavaScript
109 lines
2.9 KiB
JavaScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import process from "node:process";
|
|
|
|
import { parse } from "@mermaid-js/parser";
|
|
|
|
function isMarkdownFile(filePath) {
|
|
const lower = filePath.toLowerCase();
|
|
return lower.endsWith(".md") || lower.endsWith(".markdown");
|
|
}
|
|
|
|
function* walkFiles(rootPath) {
|
|
const stat = fs.statSync(rootPath);
|
|
if (stat.isFile()) {
|
|
yield rootPath;
|
|
return;
|
|
}
|
|
|
|
const entries = fs.readdirSync(rootPath, { withFileTypes: true });
|
|
for (const ent of entries) {
|
|
const full = path.join(rootPath, ent.name);
|
|
if (ent.isDirectory()) {
|
|
yield* walkFiles(full);
|
|
} else if (ent.isFile()) {
|
|
yield full;
|
|
}
|
|
}
|
|
}
|
|
|
|
function extractMermaidBlocks(markdownText) {
|
|
const blocks = [];
|
|
const re = /```mermaid\s*([\s\S]*?)```/g;
|
|
let m;
|
|
while ((m = re.exec(markdownText)) !== null) {
|
|
blocks.push(m[1] || "");
|
|
}
|
|
return blocks;
|
|
}
|
|
|
|
function detectDiagramType(code) {
|
|
const lines = String(code || "")
|
|
.replace(/\r\n?/g, "\n")
|
|
.split("\n")
|
|
.map((l) => l.trim())
|
|
.filter((l) => l && !l.startsWith("%%"));
|
|
|
|
if (!lines.length) return null;
|
|
const head = lines[0];
|
|
|
|
if (head.startsWith("pie")) return "pie";
|
|
if (head.startsWith("gitGraph")) return "gitGraph";
|
|
if (head.startsWith("architecture")) return "architecture";
|
|
if (head.startsWith("packet")) return "packet";
|
|
if (head.startsWith("info")) return "info";
|
|
if (head.startsWith("radar")) return "radar";
|
|
if (head.startsWith("treemap")) return "treemap";
|
|
|
|
// Not supported by @mermaid-js/parser yet (example: flowchart/sequence/class).
|
|
return null;
|
|
}
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
const roots = args.length ? args : ["reports", "docs"];
|
|
|
|
let ok = true;
|
|
let total = 0;
|
|
let failures = 0;
|
|
let skipped = 0;
|
|
|
|
for (const root of roots) {
|
|
for (const filePath of walkFiles(root)) {
|
|
if (!isMarkdownFile(filePath)) continue;
|
|
|
|
const text = fs.readFileSync(filePath, "utf8");
|
|
const blocks = extractMermaidBlocks(text);
|
|
if (!blocks.length) continue;
|
|
|
|
for (let i = 0; i < blocks.length; i++) {
|
|
const code = String(blocks[i] || "").trim();
|
|
total += 1;
|
|
const diagramType = detectDiagramType(code);
|
|
if (!diagramType) {
|
|
skipped += 1;
|
|
continue;
|
|
}
|
|
try {
|
|
await parse(diagramType, code);
|
|
} catch (err) {
|
|
ok = false;
|
|
failures += 1;
|
|
const msg =
|
|
err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
|
|
console.error(`[mermaid] ${filePath} block=${i + 1} type=${diagramType}: ${msg}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
console.log(JSON.stringify({ ok: true, diagrams: total, skipped }, null, 2));
|
|
return 0;
|
|
}
|
|
|
|
console.error(JSON.stringify({ ok: false, diagrams: total, failures, skipped }, null, 2));
|
|
return 1;
|
|
}
|
|
|
|
process.exitCode = await main();
|