From a8508ff587142fde5c30c57015f2c2051ffd4f6d Mon Sep 17 00:00:00 2001 From: "s.zotov" Date: Sun, 12 Apr 2026 20:46:37 +0000 Subject: [PATCH] Add "Rendering" --- Rendering.md | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 Rendering.md diff --git a/Rendering.md b/Rendering.md new file mode 100644 index 0000000..b186e6c --- /dev/null +++ b/Rendering.md @@ -0,0 +1,127 @@ +# Рендер и постобработка + +## Сборка 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 + +### Формат +``` +{текст прехедера} ⠀×130 +``` + +- Текст прехедера — основное содержимое +- `` — тег для Mail.ru (snippet boundary) +- `⠀×130` — невидимые символы-пустышки, чтобы почтовые клиенты не показывали текст письма после прехедера + +### Цепочка обработки +1. В Pug: `+preheader("текст")` (миксин из mixins.pug) +2. Миксин генерирует `#MAILRU_PREHEADER_TAG#` в HTML +3. server.js заменяет `#MAILRU_PREHEADER_TAG#` → `` + +### Санитизация preheader (защита от инъекций) +```javascript +preheader.replace(/[\r\n`\\]/g, '').replace(/"/g, '\\"') +``` +Убираются переносы строк (предотвращает выход из строки `+preheader("...")`), backtick и backslash. + +## Nowrap (висячие предлоги) + +Функция `applyNowrap(html)` — оборачивает короткие предлоги/союзы с следующим словом в ``. + +### Алгоритм +1. Находит только `` элементы с классом `h3`, за которыми `` +2. Для каждого совпадения вызывает `wrapShort(text)`: + - Regex: `(?...\u00A0...` +4. Placeholder нужен чтобы не было двойной обработки + +### Пример +``` +Идеи на каждый день → Идеи на каждый день +``` + +## Mindbox Tag Processing + +Функция `processMindboxTags(html, feedUrl)`: + +### Что обрабатывает +1. **Рекомендации** — удаляет `@{for...}@{end for}` блоки +2. **Переменные** — резолвит `@{set var = value}` +3. **Условия**: + - `@{if ...DiscountPercent > 0}` — скрывает блок если нет скидки + - `@{if ...OldPrice > ...Price}` — скрывает если нет реальной скидки + - Вложенные условия поддерживаются +4. **Подстановки**: + - `${Products...GetByValue('name')}` → название товара из фида + - `${formatDecimal(price)}` → форматированная цена с пробелами + - `${ResizeImage(url, width, height)}` → URL картинки +5. **Бейджи** — если товар не в наличии, на картинку накладывается полупрозрачный оверлей "Нет в наличии" + +### Маппинг свойств 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 + +```html +