В Markdown синтаксис для зачеркнутого текста реализуется с помощью двойного тильда:
~~Это зачеркнутый текст~~
При парсинге такой текст преобразуется в структуру AST
(Abstract Syntax Tree), где элемент delete или
del обозначает узел зачеркнутого текста. Remark и Rehype
предоставляют возможности для работы с этими узлами на разных этапах
обработки.
Remark работает с Markdown на уровне синтаксического
дерева. Для зачеркнутого текста важно понимать, что Remark представляет
его через узел типа delete:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const markdown = '~~Зачеркнутый текст~~';
const processor = unified()
.use(remarkParse)
.use(remarkStringify);
const tree = processor.parse(markdown);
console.log(tree);
В результате структура узла будет выглядеть примерно так:
{
"type": "root",
"children": [
{
"type": "paragraph",
"children": [
{
"type": "delete",
"children": [
{ "type": "text", "value": "Зачеркнутый текст" }
]
}
]
}
]
}
Ключевые моменты:
delete хранит текст внутри свойства
children.remarkStringify автоматически преобразует узел обратно
в Markdown с тильдами.Rehype отвечает за работу с HTML. После парсинга
Markdown с Remark можно использовать remark-rehype для
конвертации в HTML. Зачеркнутый текст в HTML представляется тегом
<del>.
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
const markdown = '~~Зачеркнутый текст~~';
const html = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.process(markdown);
console.log(html.toString()); // <p><del>Зачеркнутый текст</del></p>
Особенности:
delete в Remark автоматически преобразуется в
<del> при переходе к Rehype.<del>, например, для стилизации или добавления
атрибутов.Для тонкой настройки преобразования можно писать плагины для Remark и Rehype:
deletefunction remarkDeleteModifier() {
return (tree) => {
visit(tree, 'delete', (node) => {
node.children.forEach(child => {
if (child.type === 'text') {
child.value = `[ЗАЧЕРКНУТО] ${child.value}`;
}
});
});
};
}
import { visit } from 'unist-util-visit';
const processed = await unified()
.use(remarkParse)
.use(remarkDeleteModifier)
.use(remarkStringify)
.process('~~Пример~~');
console.log(processed.toString()); // ~~[ЗАЧЕРКНУТО] Пример~~
<del>function rehypeDelClass() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'del') {
node.properties = { className: ['strikethrough'] };
}
});
};
}
import { visit } from 'unist-util-visit';
const htmlWithClass = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeDelClass)
.use(rehypeStringify)
.process('~~Важно~~');
console.log(htmlWithClass.toString()); // <p><del class="strikethrough">Важно</del></p>
~~ как зачеркнутого текста
(delete).delete превращается в <del>.<del> лучше делать через Rehype,
чтобы не трогать Markdown напрямую.remarkStringify корректно
восстанавливает тильды для delete.Зачеркнутый текст в Remark и Rehype представляет собой удобную точку расширяемости: его можно анализировать, модифицировать, стилизовать и преобразовывать, сохраняя совместимость с Markdown и HTML.