Repository navigation
fix(ssr): escape style/script children under svg and math (#4704) - #4722
Conversation
) In HTML, <style> and <script> are raw text. In SVG and MathML content they are ordinary elements whose text parses as markup and character references, so SSR now escapes their children there like any other text. The namespace is the nearest JSX ancestor that settles it: <svg>/<math>, SVG/MathML elements and xmlns are foreign; HTML integration points (foreignObject, desc, title, the MathML text integration points, annotation-xml with an HTML encoding) and breakout tags are HTML. Components are transparent, and an element with nothing deciding stays HTML. Babel reads it in preprocess while the JSX tree is whole; the Oxc compiler runs the same pass before lowering. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117BoWNSSgtemsSYsaX2hXE
🦋 Changeset detectedLatest commit: d934522 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Size (brotli, eager entry chunk)
|
|
Generated by Claude Code |
|
The Generated by Claude Code |
Coverage Report for CI Build 42470018619Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Warning No base build found for commit Coverage: 78.945%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
Fixes #4704.
Summary
In HTML,
<style>and<script>are raw text. Under<svg>and<math>they are ordinary elements, and their text parses as markup and character references. SSR used to write every<style>/<script>child raw no matter where it sat. So onnext, a text hole like<math><style>{x}</style></math>, or the same thing under<svg>, could close the element and render the rest ofxas markup. 1.x had already fixed the SVG case (dom-expressions#590), but that fix never came over to 2.0.Now both compilers render raw text only for HTML-namespace
<style>/<script>, and they escape in foreign content. The nearest JSX ancestor that settles the namespace decides:<svg>,<math>, any SVG or MathML element (so a<g>partial counts), and a staticxmlns.foreignObject,desc,title), MathML's text integration points (mi,mo,mn,ms,mtext),annotation-xmlwith an HTMLencoding, and the tags that break out of foreign content (div,p,span, …).<svg><Show>…<style>escapes.<style>stays raw, as it is today.Babel reads the namespace in
preprocess, because an outer template is lowered before the JSX inside its expressions. This keeps<svg>{show && <style>{x}</style>}</svg>correct. The Oxc compiler runs the same pass (ssr/foreign.rs) before lowering. Both cover the template path and the spread (ssrElement) path.DOM output is unchanged. Static DOM templates on
nextalready escape correctly, and dynamic children are text nodes.Size: this is a compile-time change only, so no runtime bytes are affected on client or server.
Rules check: this is a compiler escaping fix. It touches no reactive, async or hydration semantics in RULES-FUNDAMENTALS/RULES-DERIVED, so there are no conflicts.
How did you test this change?
SVGSSR fixture: svg, math, a<g>partial, through an expression, through<Show>,foreignObject,annotation-xml encoding="text/html", and HTML<style>staying raw. The Oxc compiler's SVG fixture is regenerated from the same source, and its output matches Babel.packages/web/test/server/foreign-raw-text-4704.spec.tsxcovers the issue's repros throughrenderToString, and it passes with bothJSX_COMPILER=babeland the native compiler. It fails onnext.vitest run(422 passed).cargo test,cargo clippy(clean), andvitest run(6894 passed).Generated by Claude Code