#!/usr/bin/env node // ============================================================================== // 从已生成的 HTML 报告中提取所有 slide section 的 HTML 内容 // 用途:PowerShell GBK 编码损坏 chapter 文件后的快照式恢复 // 输出:scripts/tools/voc-validation/_slides-snapshot.json // { // "method-1": { html: "...", tone: "green", slideNum: "03/42" }, // ... // } // ============================================================================== const fs = require('fs'); const path = require('path'); const HTML_PATH = path.join('reports', 'jiangzhong-liver-voc-validation-report.html'); const OUT_PATH = path.join('scripts', 'tools', 'voc-validation', '_slides-snapshot.json'); function main() { const html = fs.readFileSync(HTML_PATH, 'utf8'); // 用正则定位每个
// 由于 section 里可能嵌套 section(eg. cover-hero 不是),但 report-section 不嵌套, // 采用首尾配对方式:从每个
const openRx = /]*>/g; const closeTag = '
'; const starts = []; let m; while ((m = openRx.exec(html)) !== null) { starts.push({ id: m[1], tone: m[2], slideNum: m[3], start: m.index, headerEnd: m.index + m[0].length }); } console.log(`Found ${starts.length} sections`); const snapshot = {}; for (let i = 0; i < starts.length; i++) { const s = starts[i]; // 找 section 的结束位置:从 headerEnd 开始,下一个 section 开始前最后一个
const nextStart = i + 1 < starts.length ? starts[i + 1].start : html.length; const closeIdx = html.lastIndexOf(closeTag, nextStart); if (closeIdx < s.headerEnd) { console.warn(`[WARN] cannot find close for ${s.id}`); continue; } const fullHtml = html.slice(s.start, closeIdx + closeTag.length); snapshot[s.id] = { tone: s.tone, slideNum: s.slideNum, html: fullHtml, }; console.log(` ${s.id.padEnd(18)} ${s.tone.padEnd(8)} ${s.slideNum} (${fullHtml.length} bytes)`); } fs.mkdirSync(path.dirname(OUT_PATH), { recursive: true }); fs.writeFileSync(OUT_PATH, JSON.stringify(snapshot, null, 2), 'utf8'); console.log(`\n✓ 写入 ${OUT_PATH} (${Object.keys(snapshot).length} sections)`); } main();