From a86e7b3e68ca144fe651541dcbe549cc83c8e6f8 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 14 Jul 2026 16:39:49 -0500 Subject: [PATCH] Resolve rendering engine per plugin instance The KaTeX engine was stored on the exported function object (texmath.katex) and guarded by `if (!texmath.katex)`, so the first `md.use(texmath, {engine})` call in a process fixed the engine for every later markdown-it instance. A subsequent instance passing a different `engine` was silently ignored, making behaviour load-order dependent and letting one consumer's engine leak into others sharing the same process. Resolve the engine once per plugin instance and capture it in a closure, then thread it explicitly through texmath.render(). Each `md.use(texmath, ...)` now uses its own engine. The deprecated texmath.use(katex) API still works: the value it sets is honored only as a fallback when no per-instance engine is supplied. --- texmath.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/texmath.js b/texmath.js index ebd61d4..45206e7 100644 --- a/texmath.js +++ b/texmath.js @@ -21,27 +21,26 @@ function texmath(md, options) { katexOptions.throwOnError = katexOptions.throwOnError || false; katexOptions.macros = katexOptions.macros || options && options.macros; // ensure backwards compatibility - if (!texmath.katex) { // else ... deprecated `use` method was used ... - if (options && typeof options.engine === 'object') { - texmath.katex = options.engine; - } - else if (typeof module === "object") - texmath.katex = require('katex'); - else // artifical error object. - texmath.katex = { renderToString() { return 'No math renderer found.' } }; - } + // Resolve the rendering engine once per plugin instance and capture it in this + // closure, so each `md.use(texmath, ...)` gets its own engine instead of sharing a + // process-wide singleton. `texmath.katex`, set by the deprecated `texmath.use()` + // API, is honored only as a fallback for backward compatibility. + const engine = (options && typeof options.engine === 'object') ? options.engine + : texmath.katex ? texmath.katex + : (typeof module === "object") ? require('katex') + : { renderToString() { return 'No math renderer found.' } }; // artificial error object // inject inline rules to markdown-it for (const rule of delimiters.inline) { if (!!outerSpace && 'outerSpace' in rule) rule.outerSpace = true; md.inline.ruler.before('escape', rule.name, texmath.inline(rule)); // ! important - md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$1/,texmath.render(tokens[idx].content,!!rule.displayMode,katexOptions)); + md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$1/,texmath.render(engine,tokens[idx].content,!!rule.displayMode,katexOptions)); } // inject block rules to markdown-it for (const rule of delimiters.block) { md.block.ruler.before('fence', rule.name, texmath.block(rule)); // ! important for ```math delimiters md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$2/,escapeHTML(tokens[idx].info)) // equation number .. ? - .replace(/\$1/,texmath.render(tokens[idx].content,true,katexOptions)); + .replace(/\$1/,texmath.render(engine,tokens[idx].content,true,katexOptions)); } } @@ -126,11 +125,11 @@ texmath.block = (rule) => return res; } -texmath.render = function(tex,displayMode,options) { +texmath.render = function(engine,tex,displayMode,options) { options.displayMode = displayMode; let res; try { - res = texmath.katex.renderToString(tex, options); + res = engine.renderToString(tex, options); } catch(err) { res = escapeHTML(`${tex}:${err.message}`)