Split out from #283 (the fix for #249), where it was noted as a follow-up. Same family as #249, for the other foreign-content namespace.
What happens
With rawHtml enabled, reparse_children_into (crates/satteri-ast/src/hast/from_html.rs) reparses an MDX JSX element's children in an <svg> fragment context when the element is named svg (#283), and in the HTML <template> context otherwise. A JSX element named math falls into the second case, so raw MathML under it is read as HTML: /> means nothing on an unknown HTML element, so <mspace width="1em"/> stays open and swallows the following siblings. Inside a real raw <math> html5ever enters the MathML namespace and handles the self-closing tag.
Attribute names are unaffected: MathML attributes use the HTML schema in property-information and here, so unlike #249 this is structural only (self-closing, nesting, and the MathML text/HTML integration points).
Repro
npx tsx repro.mts from packages/satteri:
import { mdxToJs, markdownToHast, defineMdastPlugin } from "satteri";
const inject = defineMdastPlugin({
name: "inject",
paragraph() {
return { type: "html", value: '<mspace width="1em"/><mi>x</mi>' };
},
});
console.log(
mdxToJs("<math>\n\ntext\n\n</math>\n", { mdastPlugins: [inject], features: { rawHtml: true } }).code,
);
console.log(JSON.stringify(markdownToHast('<math><mspace width="1em"/><mi>x</mi></math>', { features: { rawHtml: true } })));
Output (main and #283):
_jsx(_components.math, { children: _jsx(_components.mspace, {
width: "1em",
children: _jsx(_components.mi, { children: "x" })
}) })
versus the same markup inside raw <math>, where mspace and mi are siblings:
{"tagName":"math","children":[{"tagName":"mspace","properties":{"width":"1em"},"children":[]},{"tagName":"mi","children":[{"type":"text","value":"x"}]}]}
Reach
Not reachable from MDX markup alone (HTML-looking syntax is JSX there). It needs a plugin-injected html node as a child of a JSX <math> element, compiled with features: { rawHtml: true }.
Sketch of a fix
The narrow version mirrors #283: add HtmlSpace::MathMl with a MathML-namespace <math> context element, and enter it for a JSX element named math. But the context is currently a single in_svg: bool threaded through EmitTask, emit, emit_arena_node, and reparse_children_into, and a second namespace does not fit a bool. The shape that scales is to thread the enclosing element's QualName instead (for a stitch, the parsed parent's own name; for a JSX element, its name in the inherited namespace; <template> for component names), hand it to parse_fragment as the context element, and derive in_svg for the serializer from it. That also lets html5ever decide the integration points (foreignObject, annotation-xml, MathML text integration points) instead of the hand-written is_html_integration_point list from #283.
Test coverage
None: the from_html.rs reparse tests and the rawHtml conformance cases have no MathML-under-MDX case; the existing mathml conformance case has the <math> tag inside the raw text itself.
Split out from #283 (the fix for #249), where it was noted as a follow-up. Same family as #249, for the other foreign-content namespace.
What happens
With
rawHtmlenabled,reparse_children_into(crates/satteri-ast/src/hast/from_html.rs) reparses an MDX JSX element's children in an<svg>fragment context when the element is namedsvg(#283), and in the HTML<template>context otherwise. A JSX element namedmathfalls into the second case, so raw MathML under it is read as HTML:/>means nothing on an unknown HTML element, so<mspace width="1em"/>stays open and swallows the following siblings. Inside a real raw<math>html5ever enters the MathML namespace and handles the self-closing tag.Attribute names are unaffected: MathML attributes use the HTML schema in property-information and here, so unlike #249 this is structural only (self-closing, nesting, and the MathML text/HTML integration points).
Repro
npx tsx repro.mtsfrompackages/satteri:Output (
mainand #283):versus the same markup inside raw
<math>, wheremspaceandmiare siblings:{"tagName":"math","children":[{"tagName":"mspace","properties":{"width":"1em"},"children":[]},{"tagName":"mi","children":[{"type":"text","value":"x"}]}]}Reach
Not reachable from MDX markup alone (HTML-looking syntax is JSX there). It needs a plugin-injected
htmlnode as a child of a JSX<math>element, compiled withfeatures: { rawHtml: true }.Sketch of a fix
The narrow version mirrors #283: add
HtmlSpace::MathMlwith a MathML-namespace<math>context element, and enter it for a JSX element namedmath. But the context is currently a singlein_svg: boolthreaded throughEmitTask,emit,emit_arena_node, andreparse_children_into, and a second namespace does not fit a bool. The shape that scales is to thread the enclosing element'sQualNameinstead (for a stitch, the parsed parent's own name; for a JSX element, its name in the inherited namespace;<template>for component names), hand it toparse_fragmentas the context element, and derivein_svgfor the serializer from it. That also lets html5ever decide the integration points (foreignObject,annotation-xml, MathML text integration points) instead of the hand-writtenis_html_integration_pointlist from #283.Test coverage
None: the
from_html.rsreparse tests and the rawHtml conformance cases have no MathML-under-MDX case; the existingmathmlconformance case has the<math>tag inside the raw text itself.