import { readFile } from 'node:fs/promises';
import { cleanReaderHtml, readerBodyText } from '../lib/editorial-content.mjs';
import { editorialRevisions } from '../data/editorial-revisions.mjs';
const archive = JSON.parse(await readFile(new URL('../data/articles.json', import.meta.url), 'utf8'));
const revisionBySlug = new Map(editorialRevisions.map((revision) => [revision.slug, revision]));
const articles = archive.map((article) => ({ ...article, ...revisionBySlug.get(article.slug) }));
const hardPatterns = [
/synthetic-plan-hand-off/i,
/productionEffect/i,
/future-only/i,
/plan\/scenario/i,
/source cutoff/i,
/editorial date/i,
/planDate/i,
/not-collected/i,
/not-attempted/i,
/future owner/i,
/author trajectory/i,
/развитие автора/i,
/В современном мире/i,
/следует отметить/i,
/нужно понимать, что/i,
];
function count(content, expression) {
return (content.match(expression) || []).length;
}
function proseWithoutCode(content) {
return cleanReaderHtml(content)
.replace(/
Проверяемые источники<\/h2>[\s\S]*?(?=|$)/, '')
.replace(/
[\s\S]*?<\/table>/g, '')
.replace(/[\s\S]*?<\/figure>/g, '')
.replace(//g, '')
.replace(/[\s\S]*?<\/ul>/g, '')
.replace(/[\s\S]*?<\/ol>/g, '')
.replace(/<\/(?:p|h2|h3)>/g, '. ')
.replace(/[\s\S]*?<\/code>/g, '')
.replace(/```[\s\S]*?```/g, ' ')
.replace(/<[^>]+>/g, ' ')
.replace(/&(?:quot|amp|lt|gt|#039);/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
const failures = [];
const longSentenceWarnings = [];
let totalBodyCharacters = 0;
for (const article of articles) {
const content = cleanReaderHtml(article.contentHtml || '');
const body = readerBodyText(content);
totalBodyCharacters += body.length;
const issues = [];
if (body.length < 5000 || body.length > 15000) issues.push('body вне 5 000–15 000 знаков');
if (count(content, //g) < 1) issues.push('нет рисунка');
if (count(content, //g) < 1) issues.push('нет таблицы');
if (count(content, //g) < 1) issues.push('нет кода');
if (count(content, //g) < 1) issues.push('нет порядка действий');
const meta = hardPatterns.find((pattern) => pattern.test(body));
if (meta) issues.push('внутренняя мета-лексика: ' + meta);
if (issues.length) failures.push({ slug: article.slug, issues });
const sentences = proseWithoutCode(content)
.split(/[.!?]+\s+/)
.map((sentence) => sentence.trim())
.filter(Boolean);
const overlong = sentences
.map((sentence) => ({ words: sentence.split(/\s+/).length, sample: sentence.slice(0, 180) }))
.filter((sentence) => sentence.words > 45)
.sort((left, right) => right.words - left.words);
if (overlong.length) longSentenceWarnings.push({ slug: article.slug, count: overlong.length, maxWords: overlong[0].words, sample: overlong[0].sample });
}
console.log(JSON.stringify({
articles: articles.length,
totalBodyCharacters,
failures,
longSentenceWarnings: longSentenceWarnings.sort((left, right) => right.maxWords - left.maxWords).slice(0, 20),
longSentenceWarningCount: longSentenceWarnings.reduce((sum, item) => sum + item.count, 0),
}, null, 2));
if (failures.length) process.exitCode = 1;