fix(markdown): stop currency dollars rendering as KaTeX inline math (#5132)

This commit is contained in:
mashallow
2026-07-11 20:45:57 +07:00
committed by GitHub
parent 7f3fd77121
commit b571c7ddc1
2 changed files with 48 additions and 4 deletions
+5 -2
View File
@@ -661,8 +661,11 @@ export function mdToHtml(src, opts) {
return placeholder;
} catch (e) { return match; }
});
// Inline math: $...$ (not preceded/followed by $ or digit, not spanning multiple lines)
s = s.replace(/(?<!\$)\$(?!\$)([^\$\n]+?)\$(?!\$)/g, (match, math) => {
// Inline math: $...$ — single line only, and Pandoc-style delimiter rules so
// currency doesn't render as math ("$5 to $10"): the opening $ must be
// immediately followed by a non-space, the closing $ must be immediately
// preceded by a non-space and not followed by a digit.
s = s.replace(/(?<![\$\d])\$(?!\$)(?=\S)([^\$\n]+?)(?<=\S)\$(?!\$|\d)/g, (match, math) => {
try {
const raw = math.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
+43 -2
View File
@@ -18,12 +18,24 @@ def node_available():
pytest.skip("node binary not on PATH")
def _run_markdown_case(markdown: str, render_expr: str = "mod.mdToHtml(input)"):
def _run_markdown_case(markdown: str, render_expr: str = "mod.mdToHtml(input)", with_katex: bool = False):
script = textwrap.dedent(
r"""
import fs from 'node:fs';
globalThis.window = { location: { origin: 'http://localhost' }, katex: null };
if (__WITH_KATEX__) {
// Minimal stand-in for the CDN katex global: wraps the source so tests
// can assert what was (or wasn't) handed to KaTeX.
const katexStub = {
renderToString(src, opts) {
const display = !!(opts && opts.displayMode);
return `<span class="katex" data-display="${display}">${src}</span>`;
},
};
globalThis.window.katex = katexStub;
globalThis.katex = katexStub;
}
globalThis.document = {
readyState: 'loading',
addEventListener() {},
@@ -77,7 +89,9 @@ def _run_markdown_case(markdown: str, render_expr: str = "mod.mdToHtml(input)"):
const input = JSON.parse(process.argv[1]);
console.log(JSON.stringify({ html: __RENDER_EXPR__ }));
"""
).replace("__RENDER_EXPR__", render_expr)
).replace("__RENDER_EXPR__", render_expr).replace(
"__WITH_KATEX__", "true" if with_katex else "false"
)
result = subprocess.run(
["node", "--input-type=module", "-e", script, json.dumps(markdown)],
cwd=_REPO,
@@ -200,6 +214,33 @@ def test_inline_code_content_is_html_escaped(node_available):
assert "<b>" not in html
def test_currency_dollar_amounts_are_not_rendered_as_math(node_available):
# "$5 to $10" used to pair the two dollar signs as inline-math delimiters
# and render "5 to" through KaTeX. Pandoc-style rules now reject it: the
# closing $ is preceded by a space and followed by a digit.
html = _run_markdown_case(
"The price rose from $5 to $10 overnight.", with_katex=True
)
assert 'class="katex"' not in html
assert "$5" in html
assert "$10" in html
def test_inline_math_still_renders_through_katex(node_available):
html = _run_markdown_case("Pythagoras: $x^2 + y^2 = z^2$ holds.", with_katex=True)
assert '<span class="katex" data-display="false">x^2 + y^2 = z^2</span>' in html
assert "$" not in html
def test_display_math_still_renders_through_katex(node_available):
html = _run_markdown_case("$$\\frac{a}{b}$$", with_katex=True)
assert 'data-display="true"' in html
assert "$$" not in html
def test_dotted_python_import_paths_are_not_autolinked(node_available):
html = _run_markdown_case(
"from imblearn.combine import SMOTETomek\n"