Hi, I am using Jekyll and Kramdown to render posts in written in markdown to HTML. My posts contains a lot of math. I am using KaTeX gem. This is in my _config.yml
file:
kramdown:
math_engine: katex
It works but its that this is slow as hell. Whole blog takes around 250 seconds to render. It is caused by using execjs
library to run katex.js
. This is part of the code:
ExecJS.compile(open(PATH_TO_JS).read).call("katex.renderToString", toRender, {displayMode: dm))
I have found guy on github who made Node.JS it repeatedly. I implemented his solution and render time dropped from 250 seconds to 6 seconds. But when I saw the code itself my eyes started bleeding. His solution uses stdio
to communicate with Node.JS server. This is example of his code:
_plugins/katex.rb:
require 'execjs'
module Jekyll
module Tags
class KatexBlock < Liquid::Block
def initialize(tag, markup, tokens)
super
@markup = markup
end
def render(context)
File.open("scripts/katex_server/in", 'w') {
|file| file.write("#{@markup}\n" + super(context))
}
return File.read("scripts/katex_server/out")
end
end
end
end
Liquid::Template.register_tag('latex', Jekyll::Tags::KatexBlock)
scripts/katex_server/server:
#!/bin/node
const katex = require('./katex.min.js');
const readline = require('readline');
const fs = require('fs');
const path = require('path');
function recursive_stream() {
var is = fs.createReadStream(path.join(__dirname, 'in'));
is.on("data", function(data) {
request = data.toString();
parameters = request.split("\n")[0];
request = request.substring(parameters.length + 1);
parameters = parameters.trim().split(/\s"/);
os = fs.createWriteStream(path.join(__dirname, 'out'))
os.end(katex.renderToString(request, {
displayMode: parameters.includes("display")
}));
recursive_stream();
});
}
recursive_stream();
How to run Node.JS server INSIDE ruby script? How to use it to render latex using katex.js
?
Thank you for your help
As far as I understood your problem from the description, you need to find a way to use katex.js
in your ruby app.
For this you don't have to run node.js inside of your ruby application. Instead, you can create another web service in node.js which would provide HTTP endpoint for use of this library. If in the future you decide to use docker and kube.netes, you will have a great horizontal scalability out of the box with this approach. This approach also decouples the services quite nicely, unlike the solution of embedding one into the other, which introduces a lot of dependencies on your ruby server.
To create such a web service you don't even need to change his code a lot, just define the endpoint and wrap his code into it.
Then, on the ruby side you would have an http call, like Faraday.get('url_of_your_service').body
to get the rendered template. This would work fast, because the nodejs web server would mostly be either on the same machine or in the same cluster, in case you use something like kube.netes/aws.
Also you could process the markdown once and cache the end result and just display that. Depending on your read/write ratio this could solve your speed problem.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.