Блочный редактор вместо Markdown: почему у моего блога нет разметки в тексте
Markdown — очевидный выбор для блога. Я его не взял, и вот честный разбор: что теряется, что выигрывается и почему две модели контента хуже одной.
Когда я добавлял блог к существующему сайту, самым быстрым путём был Markdown: поле, парсер, готово. Я выбрал блоки — те же, из которых собраны все остальные страницы. Решение неочевидное, поэтому объясняю.
Одна модель контента вместо двух
К моменту появления блога на сайте уже был редактор блоков: девятнадцать видов, черновики, история версий, медиатека, поля с подсказками. Добавить Markdown значило получить второй способ верстать текст, второй набор стилей и второй путь для картинок.
Через полгода это чинится по очереди: поправил в блоках — забыл в статьях. Одна модель дороже на входе и дешевле всё остальное время.
Текст в полях — простой текст, и это защита
Абзацы у меня рендерятся как обычный текст, без разбора разметки. Значит, ничего, набранное в поле, не может внедрить теги. Обратная сторона: ссылку в тексте поставить нельзя — и это заметное ограничение, ради которого пришлось завести отдельный блок ссылок.
Звучит как костыль, но у него есть свойство: все адреса, куда ведёт статья, — это поля, а не строчки внутри прозы. Их видно списком, их легко проверить.
| Что | Markdown | Блоки |
|---|---|---|
| Порог входа | ниже | выше |
| Вёрстка сложного | нужен HTML внутри | блок под задачу |
| Картинки | путь руками | медиатека и выбор |
| Безопасность | нужен санитайзер | текст остаётся текстом |
| Переиспользование | только в статьях | на любой странице |
Блок кода: почему без подсветки
Технический блог без блока кода не живёт, и он был первым, что я добавил. Подсветку синтаксиса не сделал сознательно.
Подсветка требует токенизатора. Токенизатор на регулярках ошибается предсказуемо: незакрытая строка съедает половину файла, ключевое слово находится внутри имени переменной. Неправильно подсвеченный код в статье про код хуже одноцветного — он врёт о структуре того, что читатель пытается понять.
Поэтому блок делает то, что нельзя сделать неправильно: имя файла, язык подписью, номера строк, которые не попадают в выделение, и кнопка «скопировать», отдающая код без отступов, унаследованных от чужого файла.
1/** Строки фрагмента, с убранным общим отступом.
2 Самая неглубокая строка решает, сколько снять, — форма фрагмента выживает. */
3export function lines(code: string): string[] {
4 const rows = code.replace(/\s+$/, "").split("\n");
5 const indents = rows
6 .filter((row) => row.trim().length > 0)
7 .map((row) => row.length - row.trimStart().length);
8
9 const common = indents.length ? Math.min(...indents) : 0;
10 return rows.map((row) => row.slice(common));
11}
