Как я Markdown парсер выбирал

Вступление

Я обожаю Markdown. Это мощный, но вместе с тем лаконичный язык разметки. В его основе лежит концепция разделения данных и представления, что делает его очень удобным в ряде применений, например в системах контроля версий. Поэтому, например, Markdown является стандартом для документации на GitHub.

Markdown широко распространен в вебе как язык разметки для текстовых редакторов: на сайтах для ведения блогов, в вики проектах и т. д. Я сам ежедневно использую Markdown, и не только в разработке ПО, но и для ведения заметок. Я использую программу Obsidian: ide-подобный текстовый редактор Markdown для управления базой знаний.

Вообще говоря, Obsidian — одна из лучших программ для ведения заметок. Если вы еще не слышали о ней или о принципе zettelkasten, то, возможно, вам стоит заглянуть сюда и сюда.

Недавно я решил создать свой сайт, и мне понадобилось выбрать язык для разметки статей. Разумеется, я выбрал Markdown. Оставалось только определиться со всем остальным стеком.

Поискав готовые решения, я наткнулся на jekyll — генератор статических сайтов на основе Markdown. Он выглядел неплохим решением для минималистов, но, на мой взгляд, имел слишком много ограничений. В итоге я решил остаться на своем любимом фреймворке vue.js, а для конвертации Markdown в HTML использовать библиотеку. И вот тут началось самое интересное…

Выбор инструмента

Благодаря открытости, сравнительной простоте и популярности Markdown среди разработчиков, существует несколько десятков его реализаций на различных языках программирования. Далеко не полный список реализаций можно посмотреть здесь.

Когда я увидел это множество вариантов, первой мыслью было написать все самому с нуля и пальцы сами потянулись к клавиатуре, но я мужественно пересилил себя. Вместо этого я решил сравнить парсеры и выбрать лучший.

Конечно, для рендеринга статических страниц можно было бы использовать реализацию на любом языке, но я решил остановиться на pure-JavaScript решениях для большей гибкости.
Так у меня осталось 9 кандидатов:

  1. commonmark.js

  2. markdown-js

  3. markdown-it

  4. MarkdownDeep — GitHub и сайт

  5. Marked

  6. remark

  7. remarkable

  8. Showdown

  9. texts.js

Для сравнения парсеров я составил такой список параметров:

  1. лицензия

  2. инфраструктура

    1. документация

    2. наличие демо

    3. живое коммьюнити

  3. поддержка определенного подмножества синтаксиса Markdown

  4. возможность модифицировать логику работы парсера

  5. производительность

Лицензии

Итак, приступим! Начнем с лицензии.
Здесь все просто:

  1. Лицензия commonmark.js — 2-clause BSD, две зависимости, обе под MIT

  2. Лицензия markdown-js — MIT

  3. Лицензия markdown-it — MIT

  4. Лицензия MarkdownDeep — Apache 2.0

  5. Лицензия Marked — MIT, ссылается на Джона Грубера, создателя языка Markdown, распространяющего его под лицензией 3-clause BSD, что довольно мило

  6. Лицензия remark — MIT

  7. Лицензия remarkable — MIT

  8. Лицензия Showdown — MIT

  9. Лицензия texts.js — Apache 2.0

Другими словами, все проекты распространяются под свободными лицензиями, чего и следовало ожидать.

Инфраструктура

На документации останавливаться не будем: у всех проектов она имеется.

С демо дела чуть хуже:

  1. Демо commonmark.js

  2. Демо markdown-js — отсутствует

  3. Демо markdown-it

  4. Демо MarkdownDeep

  5. Демо Marked

  6. Демо remark — отсутствует

  7. Демо remarkable

  8. Демо Showdown

  9. Демо texts.js — отсутствует

Поддержку коммьюнити оценить сложно, не погрузившись в проект и не столкнувшись с трудностями. Косвенно проект можно оценить по числу звездочек на GitHub, но по этическим соображениям я не буду этого делать.

Что касается активности, то:

  1. проект markdown-js в данный момент не поддерживается, последний коммит в 2019 году

  2. texts.js — последний коммит в 2013 году

  3. remarkable — последний коммит в сентябре 2021 (в целом не так уж давно)

  4. остальные проекты имеют коммиты в этом году, так что можно считать их активными.

Синтаксис

Пожалуй, это самая важная часть. Для начала я составил список необходимой мне разметки:

Требования к разметке

  1. заголовки (h1 — h6)

  2. текстовые блоки

  3. цитаты (>)

    1. вложенные цитаты

  4. блоки кода (a = b)

  5. списки

    1. нумерованный (1.)

    2. маркированный (-)

    3. смешанный

  6. выделение текста

    1. курсив (*text*)

    2. жирный (**text**)

    3. жирный курсив (***text***)

    4. подчеркнутый (text)

    5. зачеркнутый (~~)

    6. выделение цветом (==)

    7. однострочный код (code)

    8. подстрочный регистр (a)

    9. надстрочный регистр (a)

  7. ссылки

    1. внешние (в интернет)

    2. внутренние (к заголовкам)

  8. медиа

    1. изображения

    2. эмодзи

  9. таблицы

  10. другое

    1. эскейпинг спецсимволов

    2. разделительная полоса (---)

  11. html

Для тестирования парсеров я составил текст с примерами всей необходимой разметки:

Тестовый текст в формате Markdown:

# 1. Headers

# h1
## h2
### h3
#### h4
##### h5
###### h6

# 2. Text blocks

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Text before line break
Text after line break

# 3. Quotes
> quote

>quote
> > nested quote
> - list in quote

# 4. Code blocks

```
untyped code block
```

```
escaped chars in code:
\```
```

```js
// js code
let a = 0
```

```python
# python code
print({"a":0})
```


# 5. lists

1. item-1
1. item-1
	1. item-1
	1. item-1
	- item
		- item

- item
	1. item-1
	2. item-2

# 6. Text decoration

*italic*

**bold**

***bold italic***

underscored

~~strikethrough~~

==highlighted==

`one line code`

A~subscript~

A^superscript^

# 7. Links

External link: [example.com](http://example.com)

Internal link: [link to h1](#h1)

# 8. Media

image: 

![Luke](https://habrastorage.org/webt/m_/it/vm/m_itvm5jqcvwj68gsk150c_caj0.jpeg)

emoji: ⛺  
    
            

© Habrahabr.ru