I Built a reStructuredText Compiler in TypeScript
Over the last couple of months, I spent my free time building a reStructuredText (RST) to Markdown compiler (rst-compiler
) in TypeScript. As it turns out, it is also the world's first feature-complete pure-JavaScript RST compiler publicly available on NPM.
For those confused: reStructuredText is implemented in docutils
in Python, so it's generally not used in the JS ecosystem.
Why?
Mostly for fun because I enjoy writing parsers and compilers.
Okay, the real reason was because I wanted to read the Godot documentation (written in RST) in VitePress because I hated their Sphinx theme: the sidebar navigation constantly shifts around as I click on different links, making it difficult to keep track of where I am.
Since VitePress only supports Markdown inputs, I realized there were two possible solutions:
- Use VitePress' dynamic routes and convert each RST file into Markdown on the fly inside Node. However, there were two problems with this approach:
- Calling Sphinx or PandaDoc inside Node either though a subshell or Pyodide was incredibly slow
- Using JS-based RST compilers was not possible because all of them were incomplete and were not actually able to parse non-trivial documents
- Use Sphinx or PandaDoc in standalone Python scripts to convert each RST file into Markdown file and point VitePress at the converted files instead.
Since the first option was not feasible, I went with the second option. However, after tinkering around for a while, I realized that the Sphinx output files needed so much post-processing before they can be ingested by VitePress that I was essentially just creating a Sphinx-output to Markdown compiler. As a result, I decided it would be better to just create my own RST to Markdown compiler to bypass Sphinx as an unnecessary middleman.
You can see my VitePress rendered Godot documentation here:
trinovantes.github.io/godot-docs/
You might also be wondering why did I not just port the VitePress theme to Sphinx or literally use any other Sphinx theme? Uh… let's just ignore this plot hole.