gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
73.83k stars 7.41k forks source link

Server-side rendering of math using KaTeX #12634

Open libkush opened 2 weeks ago

libkush commented 2 weeks ago

I have been wanting this feature for quite a while, since math isn't dynamic content why rely on the client to render it (as mentioned by the docs). Turns out there might be a couple of reasons:

However, these can be easily forsaken if SSR is provided as an opt-in feature.

Now there have been previous attempts to add the functionality using a goldmark plugin, but it seems that the maintainers are reluctant to add CGO dependencies to the project.

I have forked the plugin, and removed the QuickJS dependency in favor of goja - JavaScript engine in pure Go. Hence, fixing the CGO dependency problem.

There were also concerns regarding the reliability of the plugin, as mentioned in one of the posts in this forum.

To address these, I have also forked Hugo, added the plugin and built an example site with it which can be found here: The big old test page - Kush Patel. The source code for the same is present here.

The aforementioned post regarding issues with the plugin are not visible in it's standard math test which can be found in the example page.

Here are a few reasons why you might want to consider adding this feature in Hugo:

bep commented 2 weeks ago

but it seems that the maintainers are reluctant to add CGO dependencies to the project.

Just to be clear about this: The few (2) CGO dependencies in Hugo was added because they added features we felt was "must have Hugo features" at the time, and there were no other options, but it came at a high cost (implementation, maintenance, build/release complexity). This "reluctancy" isn't some vanity thing about wanting all "native Go". I'm pretty pragmatic about how to solve things, but not at any cost.

As to this issue, it's not obvious to me at the moment that the added complexity for this is worth it. For one, I suspect that spinning up a Goja VM for every little math snippet will perform poorly (this may have changed, but I did some test on this a few years back: https://github.com/bep/gojap).

libkush commented 2 weeks ago

This "reluctancy" isn't some vanity thing about wanting all "native Go". I'm pretty pragmatic about how to solve things, but not at any cost.

I never said I have a problem with that, I very much respect this decision. However, math seemed a very basic necessity for scientific blogging. And as for the complexity, I think it has been handed down to the end-user as the docs provide a 4-step guide to just get math "working" on their site. I agree they are quite easy to follow, but it is heaps better if it was a simple configuration switch to enable building math along with the markdown. Currently, what we have seems more like a workaround.

For one, I suspect that spinning up a Goja VM for every little math snippet will perform poorly

I agree to this, it is a mistake on my part. I patched it just now so that it should create a single vm and execute katexjs only once per build. I am not an experienced Go developer, sorry for the negligence.

As to this issue, it's not obvious to me at the moment that the added complexity for this is worth it

It is entirely up to you to decide whether or not it is worth. A long-term solution could be to use some kind of a project that ports katex's katex.renderToString() to native Go, but it seems like an overkill for this single job. However, if you shall decide to not add this in future, I might as well keep using my fork for building my websites. Thanks for the assistance.

bep commented 2 weeks ago

so that it should create a single vm and execute katexjs only

Just a heads up, a Goja VM is not thread safe and cannot be shared. If you look at my link above, I have outlined an approach that should be reasonable safe.

That said, we need to think about this a little, but thanks for the detailed explanations.

libkush commented 2 weeks ago

Just for a quick update, I did end up implementing your solution (with proper credit in the comments) for making it goroutine-safe. Thanks for the help.