mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-15 12:58:04 +00:00
fix(markdown): stop currency dollars rendering as KaTeX inline math (#5132)
This commit is contained in:
@@ -661,8 +661,11 @@ export function mdToHtml(src, opts) {
|
|||||||
return placeholder;
|
return placeholder;
|
||||||
} catch (e) { return match; }
|
} catch (e) { return match; }
|
||||||
});
|
});
|
||||||
// Inline math: $...$ (not preceded/followed by $ or digit, not spanning multiple lines)
|
// Inline math: $...$ — single line only, and Pandoc-style delimiter rules so
|
||||||
s = s.replace(/(?<!\$)\$(?!\$)([^\$\n]+?)\$(?!\$)/g, (match, math) => {
|
// 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 {
|
try {
|
||||||
const raw = math.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
const raw = math.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
|
const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
|
||||||
|
|||||||
@@ -18,12 +18,24 @@ def node_available():
|
|||||||
pytest.skip("node binary not on PATH")
|
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(
|
script = textwrap.dedent(
|
||||||
r"""
|
r"""
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
|
||||||
globalThis.window = { location: { origin: 'http://localhost' }, katex: null };
|
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 = {
|
globalThis.document = {
|
||||||
readyState: 'loading',
|
readyState: 'loading',
|
||||||
addEventListener() {},
|
addEventListener() {},
|
||||||
@@ -77,7 +89,9 @@ def _run_markdown_case(markdown: str, render_expr: str = "mod.mdToHtml(input)"):
|
|||||||
const input = JSON.parse(process.argv[1]);
|
const input = JSON.parse(process.argv[1]);
|
||||||
console.log(JSON.stringify({ html: __RENDER_EXPR__ }));
|
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(
|
result = subprocess.run(
|
||||||
["node", "--input-type=module", "-e", script, json.dumps(markdown)],
|
["node", "--input-type=module", "-e", script, json.dumps(markdown)],
|
||||||
cwd=_REPO,
|
cwd=_REPO,
|
||||||
@@ -200,6 +214,33 @@ def test_inline_code_content_is_html_escaped(node_available):
|
|||||||
assert "<b>" not in html
|
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):
|
def test_dotted_python_import_paths_are_not_autolinked(node_available):
|
||||||
html = _run_markdown_case(
|
html = _run_markdown_case(
|
||||||
"from imblearn.combine import SMOTETomek\n"
|
"from imblearn.combine import SMOTETomek\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user