const { test } = require('node:test'); const assert = require('node:assert'); const fs = require('node:fs'); const path = require('node:path'); const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8'); // The help pages are HTML too: the same structural walk validates them. const helpPages = ['help.html', 'help-ru.html'].map(f => ({ name: f, text: fs.readFileSync(path.join(__dirname, '..', f), 'utf8'), })); // Tags that never take an end tag. const VOID_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']); // End tags HTML allows to be omitted: a close tag may legally skip these. const OPTIONAL_END = new Set(['p', 'li', 'dt', 'dd', 'option', 'optgroup', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'rt', 'rp', 'colgroup', 'caption']); // Start tags that implicitly close the listed open element (the subset used here). const IMPLIED_END = { li: ['li'], dt: ['dt', 'dd'], dd: ['dt', 'dd'], p: ['p'], option: ['option'], optgroup: ['optgroup'], tr: ['tr'], td: ['td', 'th'], th: ['td', 'th'], thead: ['thead', 'tbody', 'tfoot'], tbody: ['thead', 'tbody', 'tfoot'], tfoot: ['thead', 'tbody', 'tfoot'], }; // The markup without comments and embedded script/style bodies: only this // participates in tag nesting. JavaScript template strings may hold // unbalanced