# applyNowrap — защита от висячих предлогов в email ## Назначение Функция `applyNowrap(html)` предотвращает «висячие» предлоги и короткие слова (≤3 букв) в email-рассылках. Оборачивает короткое слово + следующее слово в ``, чтобы они не разрывались на разные строки. **Почему нужна:** Mail.ru и некоторые другие почтовые клиенты игнорируют ` ` при переносе строк. Единственный надёжный способ — `white-space:nowrap` на ``. ## Полный код ```javascript function applyNowrap(html) { function wrapShort(text) { return text.replace( /(?]*class="[^"]*\bh3\b[^"]*"[^>]*>)([\s\S]*?)(<\/span><\/td>)/gi, (_match, open, content, close) => { // Шаг 2: Обработать текст МЕЖДУ тегами: >текст< const processed = content.replace( />([^<]+) '>' + wrapShort(t) + '<' ) // Шаг 3: Обработать текст В НАЧАЛЕ (до первого тега) const firstText = processed.replace( /^([^<]+)/, (m) => wrapShort(m) ) return open + firstText + close } ) // Шаг 4: Заменить placeholder'ы на реальные span-теги return result .replace(/\u200Bspan\u200Bnwr\u200B/g, '') .replace(/\u200B\/span\u200Bnwr\u200B/g, '') } ``` ## Алгоритм пошагово ### Шаг 1 — Выбор целевых блоков ``` /(]*class="[^"]*\bh3\b[^"]*"[^>]*>)([\s\S]*?)(<\/span><\/td>)/gi ``` Ищет `` с классом `h3` (текстовые блоки писем), закрытый ``. Три группы захвата: - `open` — открывающий тег `` - `content` — всё содержимое между открывающим и закрывающим тегами - `close` — закрывающий `` > **Адаптация для другого проекта:** заменить `h3` на нужный CSS-класс текстовых блоков. Заменить `` на ваш закрывающий паттерн. ### Шаг 2 — Обработка текста между вложенными тегами ``` />([^<]+)` и `<` (текст между HTML-тегами внутри блока). Например в `Что это дает?` поймает `Что это дает`. ### Шаг 3 — Обработка текста в начале блока ``` /^([^<]+)/ ``` Ловит текст от начала content до первого HTML-тега. Нужен отдельно, потому что regex из шага 2 ищет `>текст<`, а в начале блока нет `>` перед текстом. ### Шаг 4 — Placeholder → реальные теги ```javascript .replace(/\u200Bspan\u200Bnwr\u200B/g, '') .replace(/\u200B\/span\u200Bnwr\u200B/g, '') ``` Заменяет placeholder-маркеры на настоящие HTML-теги. **Зачем placeholder'ы?** Если бы мы сразу вставляли ``, то regex из шага 2 (`>([^<]+)<`) мог бы снова поймать текст внутри уже вставленного span'а и обработать его повторно. Маркеры `\u200B` (zero-width space) невидимы и не матчатся как `<` или `>`, поэтому двойной обработки не происходит. ## Ключевой regex — wrapShort ``` /(?` | | `$1` | Короткое слово (предлог) | | `\u00A0` | Non-breaking space между словами (двойная защита) | | `$2` | Следующее слово | | `\u200B/span\u200Bnwr\u200B` | Placeholder для `` | ## Результат на примере **Вход:** ```html С первыми весенними днями дизайнеры и стилисты ``` **Выход:** ```html С первыми весенними днями дизайнеры и стилисты ``` ## Исправленные баги ### Баг 1: ` ` не матчился (2026-03-20) **Симптом:** Nowrap-спаны не появляются в HTML, хотя функция вызывается. **Причина:** Типограф (Артемий Лебедев / любой другой) вставляет ` ` между предлогами и словами. Оригинальный regex искал только `\s+` и не находил ` `. **Фикс:** Заменить `\s+` на `(?:\s| )+`. ### Баг 2: `\b` не работает с кириллицей (2026-03-21) **Симптом:** Regex вообще ничего не матчит для кириллических предлогов (С, и, в, на, от, из, но, до, по, за, ко). **Причина:** В JavaScript `\b` (word boundary) работает **ТОЛЬКО с ASCII-символами** (`[a-zA-Z0-9_]`). Кириллица не считается «word character». Поэтому `\b` перед кириллическими буквами НИКОГДА не срабатывает. **Фикс:** Заменить `\b` на `(? ⚠️ **Правило:** Никогда не использовать `\b` для кириллицы в JavaScript. Всегда использовать lookbehind/lookahead. ## Адаптация для другого проекта 1. **Целевой CSS-класс:** Заменить `h3` в regex шага 1 на класс текстовых блоков вашего шаблона 2. **Закрывающий паттерн:** `` — подстроить под структуру вашего HTML (может быть `

`, `` и т.д.) 3. **Типограф:** Если используется типограф, regex уже обрабатывает ` `. Если нет — `(?:\s| )+` всё равно будет работать корректно (просто ` `-ветка не сработает) 4. **Вызов:** Применять к финальному HTML **после** типографа, но **до** отдачи клиенту 5. **Тестирование:** Обязательно проверять в Mail.ru — это самый строгий клиент по обработке пробелов ## Где вызывается В бэкенде (vite.config.js), в endpoint рендера email — после получения HTML из email-gen-api и обработки Mindbox-тегов: ```javascript const nowrapHtml = applyNowrap(rawHtml) const nowrapPreview = applyNowrap(mindbox.html) ``` Результаты отдаются клиенту: - `nowrapHtml` — финальный HTML для экспорта/отправки - `nowrapPreview` — HTML с подставленными товарами для превью