... — class may be "path", "path mono", "mono path", etc.
const re = /]*>([\s\S]*?)<\/code>/;
const m = body.match(re);
return m ? stripTags(m[2]) : null;
}
function extractShape(body) {
// ...
— class may be "shape", "code shape", etc.
const re = /]*>([\s\S]*?)<\/pre>/;
const m = body.match(re);
return m ? stripTags(m[2]) : null;
}
function extractListByClass(body, className) {
// ...
→ list of ... texts
const reUl = new RegExp(`]*>([\\s\\S]*?)<\\/ul>`);
const um = body.match(reUl);
if (!um) return [];
const liRe = /- ]*>([\s\S]*?)<\/li>/g;
const out = [];
let lm;
while ((lm = liRe.exec(um[1])) !== null) out.push(stripTags(lm[1]));
return out;
}
// ── Scan SDD MVCP pages ──
for (const quadrant of MVCP_QUADRANTS) {
const file = path.join(DOCS_DIR, `sdd-${quadrant}.html`);
if (!fs.existsSync(file)) {
issues.push(`Missing file: sdd-${quadrant}.html`);
continue;
}
const html = fs.readFileSync(file, 'utf8');
const blocks = findDefSections(html);
if (blocks.length === 0) {
issues.push(`sdd-${quadrant}.html: 0 definitions found`);
continue;
}
for (const b of blocks) {
if (!b.defName) {
issues.push(`sdd-${quadrant}.html: section without data-def-name`);
continue;
}
if (b.mvcp !== quadrant) {
issues.push(`sdd-${quadrant}.html: section "${b.defName}" has data-mvcp="${b.mvcp}" (expected ${quadrant})`);
}
const def = {
name: b.defName,
path: extractPath(b.body),
shape: extractShape(b.body),
contract: extractListByClass(b.body, 'contract'),
tests: extractListByClass(b.body, 'tests'),
};
// Per-field completeness
if (!def.path) issues.push(` · ${def.name} · missing path (
)`);
if (!def.shape) issues.push(` · ${def.name} · missing shape ()`);
// contract / tests are optional
manifest[quadrant].push(def);
}
}
// ── Output ──
const outPath = path.resolve(__dirname, '../build-manifest.json');
fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2));
const totals = MVCP_QUADRANTS.map(q => `${q}=${manifest[q].length}`).join(' · ');
const sum = MVCP_QUADRANTS.reduce((s, q) => s + manifest[q].length, 0);
console.log('\n=== build-manifest extraction report ===');
console.log(`Source : ${DOCS_DIR}`);
console.log(`Output : ${outPath}`);
console.log(`Totals : ${totals} → sum=${sum}`);
if (issues.length === 0) {
console.log('\n✓ All MVCP definitions extracted cleanly. No issues.');
} else {
console.log(`\n⚠ ${issues.length} issue(s):`);
for (const i of issues) console.log(' ' + i);
}
// Per-field completeness summary
console.log('\n--- Per-field completeness ---');
for (const q of MVCP_QUADRANTS) {
const defs = manifest[q];
const withPath = defs.filter(d => d.path).length;
const withShape = defs.filter(d => d.shape).length;
const withContract = defs.filter(d => d.contract.length > 0).length;
const withTests = defs.filter(d => d.tests.length > 0).length;
console.log(`${q.padEnd(8)} | ${defs.length} defs | path ${withPath}/${defs.length} | shape ${withShape}/${defs.length} | contract ${withContract}/${defs.length} | tests ${withTests}/${defs.length}`);
}