Добавлена полная документация для лендинга BROJS.RU, включая описание структуры проекта, SSG, пользовательского соглашения и команды для сборки. Реализована автоматическая генерация terms.html из terma.md. Обновлены зависимости и скрипты для сборки. Исправлены ошибки в конфигурации и добавлены новые страницы.
platform/bro-js/bro.landing/pipeline/head This commit looks good

This commit is contained in:
Primakov Alexandr Alexandrovich
2025-10-24 11:04:15 +03:00
parent e91b861415
commit 9110e79d6b
13 changed files with 3427 additions and 3022 deletions
+169
View File
@@ -0,0 +1,169 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable no-undef */
const fs = require('fs');
const path = require('path');
// Контент для главной страницы
const homeContent = `
<div>
<div style="max-height: 250px;">
<div>Loading...</div>
</div>
<h3><center>Сайт в разработке</center></h3>
</div>
`.trim();
// Функция для генерации полного HTML из terma.md
const generateTermsContent = () => {
const termaPath = path.resolve(__dirname, '../terma.md');
const termaText = fs.readFileSync(termaPath, 'utf-8');
// Парсим markdown в HTML с сохранением структуры
let html = '<div style="background: #f7fafc; min-height: 100vh; padding: 32px 0;">';
html += '<div style="max-width: 1200px; margin: 0 auto; background: white; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); border-radius: 8px; padding: 60px 80px; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;">';
const lines = termaText.split('\n');
let inList = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) {
if (inList) {
html += '</ul>';
inList = false;
}
continue;
}
// Заголовок H1
if (i === 0) {
html += '<div style="text-align: center; margin-bottom: 40px;">';
html += `<h1 style="font-size: 2.5em; color: #2563eb; margin-bottom: 8px; font-weight: 700;">${line}</h1>`;
continue;
}
// Дата обновления
if (line.includes('Последнее обновление')) {
html += `<p style="color: #6b7280; font-size: 0.875em;">${line}</p>`;
html += '</div><hr style="border: 0; border-top: 1px solid #e5e7eb; margin: 24px 0;">';
continue;
}
// Основные разделы (начинаются с цифры и точки без подразделов)
if (/^\d+\.\s+[А-Яа-я]/.test(line)) {
if (inList) {
html += '</ul>';
inList = false;
}
const text = line.replace(/^\d+\.\s+/, '');
html += `<h2 style="font-size: 1.875em; color: #1e40af; margin-top: 40px; margin-bottom: 16px; font-weight: 600;">${line}</h2>`;
continue;
}
// Подразделы (например, 2.1., 3.2.)
if (/^\d+\.\d+\.\s+/.test(line)) {
if (inList) {
html += '</ul>';
inList = false;
}
html += `<h3 style="font-size: 1.25em; color: #374151; margin-top: 24px; margin-bottom: 12px; font-weight: 600;">${line}</h3>`;
continue;
}
// Списки (начинаются с заглавной буквы или содержат "—")
if (line.includes('—') || (i > 0 && lines[i-1].includes('через:')) || (i > 0 && lines[i-1].includes('обязуется:')) || (i > 0 && lines[i-1].includes('собирает:')) || (i > 0 && lines[i-1].includes('ответственности за:'))) {
if (!inList) {
html += '<ul style="list-style-type: disc; padding-left: 32px; margin: 12px 0;">';
inList = true;
}
// Обработка ссылок
let processedLine = line
.replace(/https?:\/\/[^\s,)]+/g, (url) => {
const cleanUrl = url.replace(/\s*,?\s*$/, '');
return `<a href="${cleanUrl}" style="color: #3b82f6; text-decoration: underline;">${cleanUrl}</a>`;
})
.replace(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
'<a href="mailto:$1" style="color: #3b82f6; text-decoration: underline;">$1</a>');
html += `<li style="margin: 8px 0; line-height: 1.75;">${processedLine}</li>`;
continue;
}
// Обычные параграфы
if (inList && !line.includes('—')) {
html += '</ul>';
inList = false;
}
// Обработка жирного текста и ссылок
let processedLine = line
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
.replace(/https?:\/\/[^\s,)]+/g, (url) => {
const cleanUrl = url.replace(/\s*,?\s*$/, '');
return `<a href="${cleanUrl}" style="color: #3b82f6; text-decoration: underline;">${cleanUrl}</a>`;
})
.replace(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
'<a href="mailto:$1" style="color: #3b82f6; text-decoration: underline;">$1</a>');
html += `<p style="margin: 12px 0; line-height: 1.75; color: #374151;">${processedLine}</p>`;
}
if (inList) {
html += '</ul>';
}
// Footer
html += '<hr style="border: 0; border-top: 1px solid #e5e7eb; margin: 48px 0 24px 0;">';
html += '<p style="text-align: center; color: #6b7280; font-size: 0.875em;">© 2025 BROJS.RU. Все права защищены.</p>';
html += '</div></div>';
return html;
};
const prerender = () => {
try {
console.log('🚀 Начинаем мульти-страничный пре-рендеринг...');
const distPath = path.resolve(__dirname, '../dist');
const indexPath = path.join(distPath, 'index.html');
// Читаем основной HTML
let indexHtml = fs.readFileSync(indexPath, 'utf-8');
// 1. Обрабатываем главную страницу
const searchString = '<div id="app"></div>';
if (indexHtml.includes(searchString)) {
indexHtml = indexHtml.replace(searchString, `<div id="app">${homeContent}</div>`);
fs.writeFileSync(indexPath, indexHtml, 'utf-8');
console.log('✅ index.html обновлен');
}
// 2. Генерируем полный контент для terms.html из terma.md
console.log('📝 Генерируем полный HTML из terma.md...');
const termsContent = generateTermsContent();
// 3. Создаем terms.html на основе index.html
let termsHtml = indexHtml
.replace(homeContent, termsContent)
.replace('<title>bro-js admin</title>', '<title>Пользовательское соглашение - BROJS.RU</title>')
.replace(
'</head>',
'<meta name="description" content="Полное пользовательское соглашение для платформы обучения фронтенд-разработке BROJS.RU. Условия использования, обработка персональных данных, права и обязанности сторон." /></head>'
);
const termsPath = path.join(distPath, 'terms.html');
fs.writeFileSync(termsPath, termsHtml, 'utf-8');
console.log('✅ terms.html создан с полным контентом');
console.log('🎉 Пре-рендеринг завершен успешно!');
console.log('📄 Созданы файлы: index.html, terms.html');
console.log('💡 terms.html содержит полный текст соглашения для SEO');
} catch (error) {
console.error('❌ Ошибка при пре-рендеринге:', error);
process.exit(1);
}
};
prerender();