Clone
Table of Contents
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.
Рендер и постобработка
Сборка PUG-кода
Функция rebuildOutput() конкатенирует блоки:
//Заголовок ← комментарий с именем блока
+spacerLine(40) ← спейсер (если addSpacing)
tr
td.paddingWrapper
span.font.h2 ТЕКСТ
//БАННЕР
+spacerLine(20)
tr
td
a(href="https://...")
img(src="https://...")
Если сертификат включён (certificateEnabled), блоки сертификата добавляются в конец.
Render Cache
- Ключ: MD5 от
slug + pug + gender + genderPaths - Хранение: Map в памяти +
render-cache.jsonна диске - Размер: LRU, максимум 30 записей
- Инвалидация: при изменении любого параметра рендера
Preheader
Формат
{текст прехедера} <vk-snippet-end>⠀×130
- Текст прехедера — основное содержимое
<vk-snippet-end>— тег для Mail.ru (snippet boundary)⠀×130— невидимые символы-пустышки, чтобы почтовые клиенты не показывали текст письма после прехедера
Цепочка обработки
- В Pug:
+preheader("текст")(миксин из mixins.pug) - Миксин генерирует
#MAILRU_PREHEADER_TAG#в HTML - server.js заменяет
#MAILRU_PREHEADER_TAG#→<vk-snippet-end/>
Санитизация preheader (защита от инъекций)
preheader.replace(/[\r\n`\\]/g, '').replace(/"/g, '\\"')
Убираются переносы строк (предотвращает выход из строки +preheader("...")), backtick и backslash.
Nowrap (висячие предлоги)
Функция applyNowrap(html) — оборачивает короткие предлоги/союзы с следующим словом в <span style="white-space:nowrap">.
Алгоритм
- Находит только
<span>элементы с классомh3, за которыми</span></td> - Для каждого совпадения вызывает
wrapShort(text):- Regex:
(?<![a-zA-Zа-яА-ЯёЁ])([a-zA-Zа-яА-ЯёЁ]{1,3})(?:\s| )+(\S+) - Слово 1-3 буквы + пробел + следующее слово
- Regex:
- Placeholder-подход: сначала вставляет
\u200B(zero-width space), затем заменяет на<span style="white-space:nowrap">...\u00A0...</span> - Placeholder нужен чтобы не было двойной обработки
Пример
Идеи на каждый день → Идеи <span style="white-space:nowrap">на каждый</span> день
Mindbox Tag Processing
Функция processMindboxTags(html, feedUrl):
Что обрабатывает
- Рекомендации — удаляет
@{for...}@{end for}блоки - Переменные — резолвит
@{set var = value} - Условия:
@{if ...DiscountPercent > 0}— скрывает блок если нет скидки@{if ...OldPrice > ...Price}— скрывает если нет реальной скидки- Вложенные условия поддерживаются
- Подстановки:
${Products...GetByValue('name')}→ название товара из фида${formatDecimal(price)}→ форматированная цена с пробелами${ResizeImage(url, width, height)}→ URL картинки
- Бейджи — если товар не в наличии, на картинку накладывается полупрозрачный оверлей "Нет в наличии"
Маппинг свойств Mindbox → Фид
| Mindbox | Фид |
|---|---|
name |
name |
vendorName |
vendor |
url |
url |
pictureUrl |
image |
price |
price |
oldPrice |
oldPrice |
description |
description |
customField.vendorCode |
vendorCode |
customField.discountPercent |
discountPercent |
| и другие... |
Concurrency Limit
Максимум 3 параллельных рендера (MAX_CONCURRENT_RENDERS).
При превышении — HTTP 429 "Слишком много параллельных рендеров, подождите".
Счётчик activeRenders обёрнут в try/finally для гарантии декремента.
Preview в iframe
<iframe sandbox="allow-same-origin" srcdoc={previewHtml} />
sandbox="allow-same-origin"— блокирует выполнение скриптов (XSS), но позволяет доступ к DOMfitPreviewFrame()— масштабирует содержимое под viewport- Zoom: 40-100%, сохраняется в профиле на сервере
- Click detection: через
contentDocument.addEventListener(не инъекция скрипта)
Quick Edit
Клик по элементу в превью → находит соответствующий блок и поле → показывает плавающий редактор.
injectPreviewClickDetection()— навешивает click listener на iframe DOMhandlePreviewElementClick({text, rect})— ищет блок по тексту (normalizeMatchText)openQuickEdit(block, field, iframeRect)— позиционирует редактор у элемента- Изменения сразу применяются к блоку, помечают превью как stale