85 lines
3.3 KiB
JavaScript
85 lines
3.3 KiB
JavaScript
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>Проверяемые источники<\/h2>[\s\S]*?(?=<h2>|$)/, '')
|
||
.replace(/<table>[\s\S]*?<\/table>/g, '')
|
||
.replace(/<figure>[\s\S]*?<\/figure>/g, '')
|
||
.replace(/<pre[\s\S]*?<\/pre>/g, '')
|
||
.replace(/<ul>[\s\S]*?<\/ul>/g, '')
|
||
.replace(/<ol>[\s\S]*?<\/ol>/g, '')
|
||
.replace(/<\/(?:p|h2|h3)>/g, '. ')
|
||
.replace(/<code>[\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, /<figure>/g) < 1) issues.push('нет рисунка');
|
||
if (count(content, /<table>/g) < 1) issues.push('нет таблицы');
|
||
if (count(content, /<pre><code>/g) < 1) issues.push('нет кода');
|
||
if (count(content, /<ol>/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;
|