Skip to content

fix(ast): keep the SVG schema for raw HTML inside an MDX <svg> element - #283

Open
gtritchie wants to merge 5 commits into
bruits:mainfrom
gtritchie:fix/issue-249
Open

fix(ast): keep the SVG schema for raw HTML inside an MDX <svg> element#283
gtritchie wants to merge 5 commits into
bruits:mainfrom
gtritchie:fix/issue-249

Conversation

@gtritchie

@gtritchie gtritchie commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Fixes #249.

Problem

With rawHtml, reparse_children_into serialized an MDX JSX element's children with in_svg = false and reparsed them in an HTML <template> fragment context. Raw HTML under a JSX element named svg therefore lost the SVG attribute schema: <path fill-rule="evenodd"/> came back with an unknown fill-rule property instead of fillRule, and a hast fillRule property was serialized unconverted and read back lowercased as fillrule.

Fix

EmitTask::Emit/EmitArena now carry the namespace context the node sits in. Parsed elements keep deriving their schema from their own namespace; a stitched MDX node inherits its parent's; an MDX element named svg puts its children in SVG content, matching the serializer's in_svg || tag == "svg" switch. reparse_children_into then serializes with the SVG schema and parses in the existing HtmlSpace::Svg context. This also covers JSX nested inside a JSX <svg> and JSX stitched back under raw <svg>…</svg>.

Unlike the serializer, the reparse exits SVG content at the HTML integration points (foreignObject, desc, title), which is how html5ever already treats those elements' direct children.

Behaviour notes

Raw HTML inside a JSX <svg> now parses the same way as raw HTML inside a real <svg>: non-breakout HTML elements (td, label, input, …) land in the SVG namespace, void elements are not void, and <script>/<style> are not raw text. The last of these already corrupts < in script text under a real <svg> on main (hast-util-to-html and rehype-raw behave the same) — tracked in #284. Raw MathML under a JSX <math> still parses in HTML content (<mspace/> does not self-close) — the same class of problem, tracked in #285.

Tests

Rust (from_html.rs): raw child of an MDX <svg>; hast element with fillRule under an MDX <svg>; JSX nested in a JSX <svg>; JSX under raw <svg>; non-svg JSX keeps the HTML schema; ordinary HTML under a JSX <svg> breaks out; JSX under raw and JSX <foreignObject> exits SVG content. JS (compile.test.ts): mdxToJs with an mdast plugin injecting an html node under <svg> asserts fillRule/strokeWidth. All failed before the fix.

With `rawHtml`, `reparse_children_into` serialized and reparsed an MDX JSX
element's children with `in_svg = false` and an HTML `<template>` fragment
context, so raw HTML under a JSX element named `svg` lost the SVG
attribute schema: `fill-rule` stayed an unknown `fill-rule` property
instead of `fillRule`, and a hast `fillRule` property serialized
unconverted and came back lowercased as `fillrule`.

Thread the namespace context through the emitter. Parsed elements still
derive it from their own namespace; a stitched MDX node inherits its
parent's, and an MDX element named `svg` puts its children in SVG
content, matching the serializer's `in_svg || tag == "svg"` switch. The
reparse then both serializes with the SVG schema and parses in an
`<svg>` fragment context.

Fixes bruits#249
The SVG context threaded through the raw-HTML reparse was sticky through
every SVG-namespace element, so an MDX node stitched back under
`<foreignObject>` (or `<desc>`/`<title>`) reparsed its own raw children
in SVG content. The parser treats those elements as HTML integration
points and reads their children as HTML, which is also how `emit`
already chooses the schema for the element's direct children.

Stop SVG content at an HTML integration point for parsed elements,
copied arena elements, and MDX elements named after one. Stickiness
through `<foreignObject>` remains a serializer-only rule in `render.rs`.

Refs bruits#249
html5ever parses <math> subtrees in the MathML namespace, not the HTML
context; the helper ignores MathML's integration points only because the
reparse context tracks SVG content alone.
GitHub's merge of main (bruits#282) spliced the two new bruits#192 tests inside the
bruits#249 test body, leaving the mdxToJs describe block unclosed so the file
failed to parse. Re-add the test's closing line; the file is now main
plus the bruits#249 test.
@codspeed-hq

codspeed-hq Bot commented Aug 22, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 36 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing gtritchie:fix/issue-249 (1ca75ea) with main (563954f)

Open in CodSpeed

Footnotes

  1. 10 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

rawHtml: reparsing children of an MDX JSX <svg> element loses the SVG context

1 participant