From aa2654d633d44b178e6e735f71ae0a8d0a6aebe4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 12 Dec 2024 01:19:30 +0000 Subject: [PATCH] fix: ensure if block paths retain correct template namespacing --- .changeset/giant-moons-accept.md | 5 ++ .../3-transform/client/visitors/IfBlock.js | 52 +++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 .changeset/giant-moons-accept.md diff --git a/.changeset/giant-moons-accept.md b/.changeset/giant-moons-accept.md new file mode 100644 index 0000000000..7940371d6f --- /dev/null +++ b/.changeset/giant-moons-accept.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure if block paths retain correct template namespacing diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index d658f9eaf8..5e9b1975b9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -1,8 +1,42 @@ /** @import { BlockStatement, Expression } from 'estree' */ -/** @import { AST } from '#compiler' */ +/** @import { AST, Namespace } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import * as b from '../../../../utils/builders.js'; +/** + * @param {AST.Fragment} fragment + * @return {Namespace} + */ +function get_namespace(fragment) { + const elements = fragment.nodes.filter((n) => n.type === 'RegularElement'); + /** @type {Namespace | null} */ + let namespace = null; + + // Check the elements within the fragment and look for consistent namespaces. + // If we have no namespaces or they are mixed, then fallback to `html` + for (const element of elements) { + const metadata = element.metadata; + + if (metadata.mathml) { + if (namespace === null || namespace === 'mathml') { + namespace = 'mathml'; + } else { + namespace = 'html'; + } + } else if (metadata.svg) { + if (namespace === null || namespace === 'svg') { + namespace = 'svg'; + } else { + namespace = 'html'; + } + } else { + namespace = 'html'; + } + } + + return namespace ?? 'html'; +} + /** * @param {AST.IfBlock} node * @param {ComponentContext} context @@ -11,7 +45,13 @@ export function IfBlock(node, context) { context.state.template.push(''); const statements = []; - const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent)); + const consequent_namespace = get_namespace(node.consequent); + const consequent = /** @type {BlockStatement} */ ( + context.visit(node.consequent, { + ...context.state, + metadata: { ...context.state.metadata, namespace: consequent_namespace } + }) + ); const consequent_id = context.state.scope.generate('consequent'); statements.push(b.var(b.id(consequent_id), b.arrow([b.id('$$anchor')], consequent))); @@ -19,7 +59,13 @@ export function IfBlock(node, context) { let alternate_id; if (node.alternate) { - const alternate = /** @type {BlockStatement} */ (context.visit(node.alternate)); + const alternate_namespace = get_namespace(node.consequent); + const alternate = /** @type {BlockStatement} */ ( + context.visit(node.alternate, { + ...context.state, + metadata: { ...context.state.metadata, namespace: alternate_namespace } + }) + ); alternate_id = context.state.scope.generate('alternate'); statements.push(b.var(b.id(alternate_id), b.arrow([b.id('$$anchor')], alternate))); }