- Add skill that uses check-mermaid.js to validate Mermaid diagrams - Skill guides Claude to parse error reports and apply intelligent fixes - Fixes common Mermaid parser bugs (parentheses, brackets, braces in labels) - Include demo scripts with test Markdown file for validation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
128 lines
4.0 KiB
JavaScript
128 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
const fs = require('fs');
|
||
const { execSync } = require('child_process');
|
||
const path = require('path');
|
||
const os = require('os');
|
||
|
||
// 获取命令行传入的 Markdown 文件路径
|
||
const fileArg = process.argv[2];
|
||
if (!fileArg) {
|
||
console.error('❌ 请提供 Markdown 文件路径。用法: node check-mermaid.js <file.md>');
|
||
process.exit(1);
|
||
}
|
||
|
||
const filePath = path.resolve(fileArg);
|
||
const fileName = path.basename(filePath);
|
||
|
||
if (!fs.existsSync(filePath)) {
|
||
console.error(`❌ 文件不存在: ${filePath}`);
|
||
process.exit(1);
|
||
}
|
||
|
||
const content = fs.readFileSync(filePath, 'utf-8');
|
||
|
||
// 正则匹配 ```mermaid ... ``` 代码块,兼容 Windows(\r\n) 和 Linux(\n)
|
||
const mermaidRegex = /```mermaid\r?\n([\s\S]*?)```/g;
|
||
let match;
|
||
let blockCount = 0;
|
||
let errorCount = 0;
|
||
|
||
// 用于收集所有格式化后的错误报告块
|
||
const errorReports = [];
|
||
|
||
while ((match = mermaidRegex.exec(content)) !== null) {
|
||
blockCount++;
|
||
|
||
const fullMatch = match[0];
|
||
const code = match[1];
|
||
|
||
// 1. 计算在 Markdown 文件中的绝对行号
|
||
const textBeforeMatch = content.substring(0, match.index);
|
||
const startLine = textBeforeMatch.split(/\r?\n/).length;
|
||
const endLine = startLine + fullMatch.split(/\r?\n/).length - 1;
|
||
|
||
// 创建临时文件存放单个 Mermaid 代码
|
||
const tmpFile = path.join(os.tmpdir(), `mermaid-check-${Date.now()}-${blockCount}.mmd`);
|
||
fs.writeFileSync(tmpFile, code.trim());
|
||
|
||
try {
|
||
// 调用 mmdc 进行静默渲染检查
|
||
execSync(`npx mmdc -i "${tmpFile}" -o "${tmpFile}.svg" -q`, { stdio: 'pipe' });
|
||
} catch (error) {
|
||
errorCount++;
|
||
|
||
// 2. 净化报错信息:剔除底层执行堆栈
|
||
const stderr = error.stderr ? error.stderr.toString() : error.message;
|
||
const errorLines = stderr.split(/\r?\n/);
|
||
const cleanErrorLines = [];
|
||
|
||
for (const line of errorLines) {
|
||
if (line.trim().startsWith('at ') ||
|
||
line.includes('Parser3.parseError') ||
|
||
line.includes('fromText')) {
|
||
break;
|
||
}
|
||
cleanErrorLines.push(line);
|
||
}
|
||
const cleanErrorOutput = cleanErrorLines.join('\n').trim();
|
||
|
||
// 3. 截断代码内容:最少3行,最多6行
|
||
const codeLines = code.split(/\r?\n/).filter(l => l.trim() !== '');
|
||
let displayCode = '';
|
||
|
||
if (codeLines.length <= 6) {
|
||
displayCode = codeLines.join('\n');
|
||
} else {
|
||
const head = codeLines.slice(0, 3).join('\n');
|
||
const tail = codeLines.slice(-3).join('\n');
|
||
displayCode = `${head}\n ...\n ... (中间省略 ${codeLines.length - 6} 行) ...\n ...\n${tail}`;
|
||
}
|
||
|
||
// 4. 组装单个错误的 Markdown 块
|
||
errorReports.push(`### ❌ 错误 #${errorCount} (代码块 #${blockCount})
|
||
|
||
- **文档位置:** 第 \`${startLine}\` 行至第 \`${endLine}\` 行
|
||
|
||
#### 核心错误详情
|
||
\`\`\`text
|
||
${cleanErrorOutput}
|
||
\`\`\`
|
||
|
||
#### 代码内容片段
|
||
\`\`\`text
|
||
${displayCode}
|
||
\`\`\``);
|
||
|
||
} finally {
|
||
// 清理临时文件
|
||
if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
|
||
if (fs.existsSync(`${tmpFile}.svg`)) fs.unlinkSync(`${tmpFile}.svg`);
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------------
|
||
// 生成最终的 Markdown 报告输出
|
||
// ------------------------------------------------------------------
|
||
|
||
if (errorCount > 0) {
|
||
// 存在错误的情况
|
||
console.log(`## 🚨 Mermaid 语法检查报告\n`);
|
||
console.log(`**检查文件:** \`${fileName}\``);
|
||
console.log(`**检查结果:** ❌ 发现 ${errorCount} 处语法错误 (共检测到 ${blockCount} 个代码块)\n`);
|
||
console.log(`---\n`);
|
||
console.log(errorReports.join('\n\n---\n\n'));
|
||
|
||
// 返回非零状态码,确保在 CI/CD 或 Git Hook 中能够阻断流程
|
||
process.exit(1);
|
||
} else {
|
||
// 全部正确或没有代码块的情况
|
||
console.log(`## 🎉 Mermaid 语法检查报告\n`);
|
||
console.log(`**检查文件:** \`${fileName}\``);
|
||
if (blockCount === 0) {
|
||
console.log(`**检查结果:** ⚠️ 未检测到 Mermaid 代码块`);
|
||
} else {
|
||
console.log(`**检查结果:** ✅ 全部通过 (共检测到 ${blockCount} 个代码块)`);
|
||
}
|
||
process.exit(0);
|
||
} |