🔄

新卒時代から約4年開発し続けてる自作 Markdown パーサー Richmd を振り返る

2024/02/19に公開

はじめに

4年前、新卒のときに、Richmd という Markdownパーサーを作りました。
当時設定したコンセプトとしては、「Markdownでさまざまな表現ができる」でした。
なぜこれを作ろうと思ったのか?それは、Detailsの記法が気持ち悪かったからです。。。

<details><summary>test</summary>
hoge
</details>

いやぁ、なんでHTML出てくるんかい! と心の中で何度も突っ込みました。。。
もっと簡単にかける Markdown記法ほしいなと思った時に、思いついたのが、自作パーサー作る方法でした。

GitHub

https://github.com/bebeji-nappa/richmd

制作初期(2020年秋ごろ)

まずは、パーサーの勉強を始めました。
パーサーの説明は長くなってしまうので割愛しますが、
以下が詳しく書かれています。
https://zenn.dev/denham/articles/01ed6a101e9b98

当時は、公開されているMarkdownパーサーをダウンロードして、自分は、HTMLコンバーターの部分を実装しようと思っていました。
で、以下にたどり着きます。
https://github.com/ysugimoto/markdown-tree-parser

抽出構文木の生成をしてくれるパッケージですが、使用して違和感を持ちました。処理を確認したら、以下の問題がありました。

  • まだ、未実装の機能が多い
  • そもそも、生成された抽出構文木がバグってた
  • しばらく、メンテナンスされてない(今見たら、もうメンテしてなさそうで時が止まってた...)

もうメンテされてない以上、p-r出しても厳しいと判断して、これのコードをベースに自前で改良することにしました。
まずは、ソースコードの解析に入りました。
字句解析と構文解析のロジックがわかったタイミングで、修正作業に入りました。
最終的にHTMLへコンバートする想定で、抽出構文木の形を修正していきました。
そして、最終的に以下の抽出構文木がJSON形式で出力されるように修正しました。

{
    "ast": [
        {
            "name": "heading",
            "type": "block",
            "level": 1,
            "values": [
                {
                    "name": "text",
                    "type": "inline",
                    "value": "aaaaaaa"
                }
            ]
        },
        {
            "name": "heading",
            "type": "block",
            "level": 2,
            "values": [
                {
                    "name": "text",
                    "type": "inline",
                    "value": "aaaaaaa"
                }
            ]
        },
        {
            "name": "br",
            "type": "block"
        },
        {
            "name": "paragraph",
            "type": "block",
            "values": [
                {
                    "name": "text",
                    "type": "inline",
                    "value": "aaaaaaaaa\n"
                }
            ]
        },
        {
            "name": "br",
            "type": "block"
        },
        {
            "name": "list",
            "type": "block",
            "level": 1,
            "values": [
                {
                    "name": "text",
                    "type": "inline",
                    "value": "aaaaaa"
                }
            ]
        },
        {
            "name": "list",
            "type": "block",
            "level": 1,
            "values": [
                {
                    "name": "text",
                    "type": "inline",
                    "value": "aaaaaa"
                }
            ]
        }
    ]
}

制作中期(2020年秋後半 - 冬頃)

さて、ここからは、HTMLコンバートの実装に入ります。
上記の抽象構文木をデータを使って、コンバーターを作成していきます。
ここは、本当に地道な作業です。
当時は、地道に if - else if - else を屈指して変換してましたねw
大変だったのは、リスト表記のインデント対応が大変でした。
また、code block はプログラミング言語対応したかったので、highlight.js を使用しました。
https://highlightjs.org/

制作後期(2020年冬頃)

ここで、開発している時に、パーサーの法則性に気づきます。
字句解析のパターンとして、

  • 一行完結タイプ
  • くくってブロック作るタイプ
  • Blockquoteタイプ

に大きく分かれることに気づきました。この法則に気づいた瞬間、「オリジナルの記法が作れるのでは?」と確信し、実際に KaTeX を使用した数式表記の Markdown記法を考えて、作ってみました。

image

これは、行ける!と思った自分は、どんどん機能を増やしていきました。
そして、それが、現在のRichmdへと繋がっていきます。

リリース後

リリース後は、JavaScriptから、TypeScriptに移行しました。
その後は機能拡張に邁進し、cli の構築をしていました。同時は .richmd という拡張子ファイルを使うことで、コンパイラを提供できるのではと考えて開発していました。

https://github.com/bebeji-nappa/richmd-cli

ただ、やはり、実用的とは思えなかった点や、普通に React, Vue で使ったほうが良いと思ったので、この開発は失敗だったと思っています。

2022年 - 2023年頭は、業務で本当に余裕がなく、とてもOSSでやれる状況ではありませんでした。
そして、今年に入り、やっとメンテナンス作業に着手しました。
メンテナンスでは、いくつかのバグを修正しつつ、ESLint, Prettier の導入もしました。
以前より、フォーマットも整ったかと思います。

今後の展望

このRichmdは、もう実装したい機能は搭載できたかと思っています。
ただ、発信力も弱かったせいか、普及率が全然だと思うので、2024年も継続的にアップデートできればと思っています。

さいごに

Richmdは誰でも簡単に導入できる Markdown パーサーです。
是非、一度使ってみてくださいー

Install

pnpm add richmd

GitHub

https://github.com/bebeji-nappa/richmd

Discussion