Библиотеки Remark и Rehype
позволяют работать с Markdown и HTML на уровне AST (Abstract Syntax
Tree), что открывает гибкие возможности для модификации содержимого
документа. Одной из часто востребованных задач является
добавление атрибутов к изображениям, таких как
alt, title, class,
loading или width/height.
В Remark изображения представлены узлами типа image.
Пример узла:
{
"type": "image",
"title": "Пример изображения",
"url": "/images/example.png",
"alt": "Описание изображения"
}
Каждое поле может быть использовано для добавления или изменения
атрибутов. В Rehype аналогичные структуры представляют HTML-теги,
например, <img> с соответствующими атрибутами.
Remark поддерживает использование плагинов для обработки AST. Один из
подходов — использовать функцию-плагин, которая модифицирует узлы
image.
Пример плагина, добавляющего класс и атрибут
loading="lazy":
const visit = require('unist-util-visit');
function addImageAttributes() {
return (tree) => {
visit(tree, 'image', (node) => {
if (!node.data) node.data = {};
if (!node.data.hProperties) node.data.hProperties = {};
node.data.hProperties.class = 'responsive-image';
node.data.hProperties.loading = 'lazy';
});
};
}
module.exports = addImageAttributes;
Здесь используется unist-util-visit для обхода AST и
добавления атрибутов через hProperties, которые позже будут
преобразованы в HTML.
Для расширенной функциональности часто используют синтаксис с фигурными скобками для Markdown:
{.responsive-image loading=lazy width=600}
Для обработки такого синтаксиса применяют плагин
remark-attr, который разбирает атрибуты в фигурных скобках
и добавляет их в AST.
Пример настройки:
const remark = require('remark');
const remarkAttr = require('remark-attr');
remark()
.use(remarkAttr)
.process('{.responsive-image loading=lazy}', (err, file) => {
console.log(String(file));
});
После обработки атрибуты будут включены в HTML тег
<img>:
<img src="/images/example.png" alt="Описание изображения" class="responsive-image" loading="lazy">
Remark работает с Markdown, а Rehype отвечает за HTML. Для интеграции
Remark и Rehype применяют remark-rehype:
const remark = require('remark');
const rehype = require('rehype');
const remarkRehype = require('remark-rehype');
const rehypeStringify = require('rehype-stringify');
remark()
.use(remarkAttr)
.use(remarkRehype)
.use(rehypeStringify)
.process('{.responsive-image loading=lazy}', (err, file) => {
console.log(String(file));
});
В результате получается готовый HTML с заданными атрибутами:
<img src="/images/example.png" alt="Описание изображения" class="responsive-image" loading="lazy">
Для более сложных случаев, например, когда атрибуты зависят от имени файла или других условий, можно использовать функцию, которая проверяет URL изображения и добавляет соответствующие атрибуты:
const visit = require('unist-util-visit');
function dynamicImageAttributes() {
return (tree) => {
visit(tree, 'image', (node) => {
node.data = node.data || {};
node.data.hProperties = node.data.hProperties || {};
if (node.url.endsWith('.png')) {
node.data.hProperties.class = 'png-image';
} else if (node.url.endsWith('.jpg')) {
node.data.hProperties.class = 'jpg-image';
}
node.data.hProperties.loading = 'lazy';
});
};
}
Такой подход позволяет гибко управлять визуальным стилем и производительностью за счет ленивой загрузки изображений.
Иногда необходимо различать локальные и внешние изображения,
например, для добавления referrerpolicy или
crossorigin:
visit(tree, 'image', (node) => {
node.data = node.data || {};
node.data.hProperties = node.data.hProperties || {};
if (node.url.startsWith('http')) {
node.data.hProperties.crossorigin = 'anonymous';
} else {
node.data.hProperties.loading = 'lazy';
}
});
Этот метод позволяет комбинировать безопасность и оптимизацию загрузки.
Добавление классов через hProperties.class упрощает
интеграцию с CSS-фреймворками. Например, для Tailwind:
node.data.hProperties.class = 'rounded-lg shadow-lg';
Это делает Markdown более выразительным без ручного редактирования HTML.
Эти методы позволяют полностью контролировать атрибуты изображений на уровне AST, обеспечивая гибкую кастомизацию и интеграцию с фронтенд-стилями и оптимизацией производительности.