Files
progcode/web/scripts/audit-style-corpus.mjs
huncode 7c5b19c960
Build and deploy / deploy (push) Successful in 18s
edit full article archive to publication standard
2026-07-31 23:08:19 +03:00

85 lines
3.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;