#!/usr/bin/env node import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const bad = [] /** * @param {string} dir * @returns {string[]} */ function walkMarkdown(dir) { /** @type {string[]} */ const out = [] for (const name of fs.readdirSync(dir)) { const p = path.join(dir, name) const st = fs.statSync(p) if (st.isDirectory()) out.push(...walkMarkdown(p)) else if (name.endsWith('.md')) out.push(p) } return out } /** * @param {string} text */ function validateMermaidBlocks(text) { /** @type {{ body: string, startLine: number }[]} */ const blocks = [] const lines = text.split(/\r?\n/) for (let i = 0; i < lines.length; i++) { if (String(lines[i]).trim() !== '```mermaid') continue const startLine = i + 1 let j = i + 1 while (j < lines.length && String(lines[j]).trim() !== '```') j++ if (j >= lines.length) { bad.push(`unclosed mermaid code fence at line ${startLine}`) break } const body = lines.slice(i + 1, j).join('\n').trim() blocks.push({ body, startLine }) i = j } for (const b of blocks) { if (!b.body) { bad.push(`empty mermaid block at line ${b.startLine}`) continue } const first = b.body.split(/\r?\n/, 1)[0].trim() if ( !/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|journey|gantt|pie|mindmap|timeline)\b/.test( first ) ) { bad.push( `invalid mermaid first line at ${b.startLine}: expected diagram type, got "${first}"` ) } } } const mdFiles = [ ...walkMarkdown(path.join(root, 'docs')), ...walkMarkdown(path.join(root, 'developer-guide')), ...walkMarkdown(path.join(root, 'handbook')), ...walkMarkdown(path.join(root, 'users-manual')) ] for (const file of mdFiles) { const raw = fs.readFileSync(file, 'utf8') const before = bad.length validateMermaidBlocks(raw) if (bad.length > before) { for (let i = before; i < bad.length; i++) { bad[i] = `${path.relative(root, file)}: ${bad[i]}` } } } if (bad.length) { console.error('validate-mermaid-syntax: failed') for (const b of bad) console.error(' ', b) process.exit(1) } console.log('validate-mermaid-syntax: OK')