From b5fefe3992545dc486b0e53d51d24a0dde5a5af9 Mon Sep 17 00:00:00 2001 From: Gregory Gundersen Date: Mon, 2 Mar 2026 22:32:30 -0500 Subject: [PATCH] Add tag caching --- lib/jekyll-katex.rb | 9 ++++ lib/jekyll-katex/cache.rb | 66 ++++++++++++++++++++++++++++++ lib/jekyll/tags/katex.rb | 4 +- lib/jekyll/tags/katex_math_mode.rb | 4 +- 4 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 lib/jekyll-katex/cache.rb diff --git a/lib/jekyll-katex.rb b/lib/jekyll-katex.rb index f9e1b37..c0ad6dc 100644 --- a/lib/jekyll-katex.rb +++ b/lib/jekyll-katex.rb @@ -3,6 +3,15 @@ require 'jekyll' require 'jekyll/tags/katex' require 'jekyll/tags/katex_math_mode' +require 'jekyll-katex/cache' Liquid::Template.register_tag('katex', Jekyll::Tags::Katex) Liquid::Template.register_tag('katexmm', Jekyll::Tags::KatexMathMode) + +Jekyll::Hooks.register :site, :after_init do |_site| + Jekyll::Katex::Cache.load_cache +end + +Jekyll::Hooks.register :site, :post_write do |_site| + Jekyll::Katex::Cache.save_cache +end diff --git a/lib/jekyll-katex/cache.rb b/lib/jekyll-katex/cache.rb new file mode 100644 index 0000000..8a96a22 --- /dev/null +++ b/lib/jekyll-katex/cache.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'digest' +require 'fileutils' +require 'jekyll-katex/katex_js' + +module Jekyll + module Katex + # Disk-based render cache for KaTeX expressions. + # + # Caches the HTML output of katex.renderToString calls in a Marshal file so + # that expressions are only rendered once. Subsequent builds serve cached + # HTML instantly instead of calling ExecJS/Node.js for every expression. + # + # Cache lives in .jekyll-cache/katex-cache.marshal (already gitignored). + # Run `jekyll clean` to clear it. + module Cache + CACHE_DIR = '.jekyll-cache' + CACHE_FILE = File.join(CACHE_DIR, 'katex-cache.marshal') + + @store = {} + @dirty = false + @hits = 0 + @misses = 0 + + class << self + def load_cache + if File.exist?(CACHE_FILE) + File.open(CACHE_FILE, 'rb') { |f| @store = Marshal.load(f) } + Jekyll.logger.info 'KaTeX Cache:', "Loaded #{@store.size} cached expressions" + else + @store = {} + Jekyll.logger.info 'KaTeX Cache:', 'No cache file found, starting fresh' + end + @dirty = false + @hits = 0 + @misses = 0 + end + + def save_cache + if @dirty + FileUtils.mkdir_p(CACHE_DIR) + File.open(CACHE_FILE, 'wb') { |f| Marshal.dump(@store, f) } + Jekyll.logger.info 'KaTeX Cache:', "Saved #{@store.size} expressions (#{@hits} hits, #{@misses} misses)" + else + Jekyll.logger.info 'KaTeX Cache:', "No new expressions, skipping write (#{@hits} hits)" + end + end + + def render(latex_source, rendering_options) + key = Digest::SHA256.hexdigest(latex_source + rendering_options.to_s) + if @store.key?(key) + @hits += 1 + @store[key] + else + @misses += 1 + result = KATEX_JS.call('katex.renderToString', latex_source, rendering_options) + @store[key] = result + @dirty = true + result + end + end + end + end + end +end diff --git a/lib/jekyll/tags/katex.rb b/lib/jekyll/tags/katex.rb index 1bc7d07..50ca003 100644 --- a/lib/jekyll/tags/katex.rb +++ b/lib/jekyll/tags/katex.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'jekyll-katex/configuration' -require 'jekyll-katex/katex_js' module Jekyll module Tags @@ -11,7 +10,6 @@ module Tags # {% endkatex %} class Katex < Liquid::Block LOG_TOPIC = 'Katex Block:' - KATEX ||= Jekyll::Katex::KATEX_JS def initialize(tag_name, markup, tokens) super @@ -23,7 +21,7 @@ def initialize(tag_name, markup, tokens) def render(context) latex_source = super rendering_options = Jekyll::Katex::Configuration.global_rendering_options.merge(displayMode: @display) - KATEX.call('katex.renderToString', latex_source, rendering_options) + Jekyll::Katex::Cache.render(latex_source, rendering_options) end end end diff --git a/lib/jekyll/tags/katex_math_mode.rb b/lib/jekyll/tags/katex_math_mode.rb index 3331e52..ce3884f 100644 --- a/lib/jekyll/tags/katex_math_mode.rb +++ b/lib/jekyll/tags/katex_math_mode.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'jekyll-katex/configuration' -require 'jekyll-katex/katex_js' module Jekyll module Tags @@ -16,7 +15,6 @@ module Tags # {% endkatexmm %} class KatexMathMode < Liquid::Block LOG_TOPIC = 'KatexMathMode:' - KATEX ||= Jekyll::Katex::KATEX_JS LATEX_TOKEN_PATTERN = /(?