fix: infer `svg` namespace correctly (#10027)

Add recursive check for logic blocks, ignore things such as ConstTags and Comments
closes #10025

---------

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/10128/head
navorite 6 months ago committed by GitHub
parent f5dc562ee7
commit cd2263fdab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: infer `svg` namespace correctly

@ -211,23 +211,67 @@ export function infer_namespace(namespace, parent, nodes, path) {
parent_node.type === 'SvelteFragment' ||
parent_node.type === 'SnippetBlock')
) {
// Heuristic: Keep current namespace, unless we find a regular element,
// in which case we always want html, or we only find svg nodes,
// in which case we assume svg.
let only_svg = true;
for (const node of nodes) {
if (node.type === 'RegularElement') {
if (!node.metadata.svg) {
namespace = 'html';
only_svg = false;
break;
}
} else if (node.type !== 'Text' || node.data.trim() !== '') {
only_svg = false;
}
const new_namespace = check_nodes_for_namespace(nodes, 'keep');
if (new_namespace !== 'keep' && new_namespace !== 'maybe_html') {
namespace = new_namespace;
}
if (only_svg) {
namespace = 'svg';
}
return namespace;
}
/**
* Heuristic: Keep current namespace, unless we find a regular element,
* in which case we always want html, or we only find svg nodes,
* in which case we assume svg.
* @param {import('#compiler').SvelteNode[]} nodes
* @param {import('#compiler').Namespace | 'keep' | 'maybe_html'} namespace
*/
function check_nodes_for_namespace(nodes, namespace) {
for (const node of nodes) {
if (node.type === 'RegularElement') {
if (!node.metadata.svg) {
namespace = 'html';
break;
} else if (namespace === 'keep') {
namespace = 'svg';
}
} else if (
(node.type === 'Text' && node.data.trim() !== '') ||
node.type === 'HtmlTag' ||
node.type === 'RenderTag'
) {
namespace = 'maybe_html';
} else if (node.type === 'EachBlock') {
namespace = check_nodes_for_namespace(node.body.nodes, namespace);
if (namespace === 'html') break;
if (node.fallback) {
namespace = check_nodes_for_namespace(node.fallback.nodes, namespace);
if (namespace === 'html') break;
}
} else if (node.type === 'IfBlock') {
namespace = check_nodes_for_namespace(node.consequent.nodes, namespace);
if (namespace === 'html') break;
if (node.alternate) {
namespace = check_nodes_for_namespace(node.alternate.nodes, namespace);
if (namespace === 'html') break;
}
} else if (node.type === 'AwaitBlock') {
if (node.pending) {
namespace = check_nodes_for_namespace(node.pending.nodes, namespace);
if (namespace === 'html') break;
}
if (node.then) {
namespace = check_nodes_for_namespace(node.then.nodes, namespace);
if (namespace === 'html') break;
}
if (node.catch) {
namespace = check_nodes_for_namespace(node.catch.nodes, namespace);
if (namespace === 'html') break;
}
} else if (node.type === 'KeyBlock') {
namespace = check_nodes_for_namespace(node.fragment.nodes, namespace);
if (namespace === 'html') break;
}
}

@ -34,6 +34,10 @@
return Promise.resolve([24, 25]);
}
const some = {
fn: () => {}
}
const update = async () => {
[a, b] = [1, await Promise.resolve(2)];
({ c = await Promise.resolve(3), d } = { d: 4 });
@ -49,7 +53,7 @@
[l = obj[await Promise.resolve("prop")] + 1] = [];
[m] = [`${1}${await Promise.resolve("3")}`];
[n] = [-(await Promise.resolve(-14))];
[o] = [(console.log(15), await Promise.resolve(15))];
[o] = [(some.fn(), await Promise.resolve(15))];
({ anotherprop: p = await Promise.resolve(16) } = obj);
let val1, val2;
({ val1 = (async function (x) { return await x; })(Promise.resolve(18)), r = await val1 }

@ -0,0 +1,13 @@
<text x="0" y=14>outside</text>
{#if true}
<text x="0" y="26">true</text>
{:else}
<text x="0" y="26">false</text>
{/if}
{#each Array(3).fill(0) as item, idx}
<text x={idx * 10} y={42}>{idx}</text>
{/each}
<!-- comment should not set infer html namespace -->

@ -0,0 +1,26 @@
import { test, ok } from '../../test';
export default test({
html: `
<svg>
<text x="0" y="14">outside</text>
<text x="0" y="26">true</text>
<text x="0" y="42">0</text>
<text x="10" y="42">1</text>
<text x="20" y="42">2</text>
</svg>
`,
test({ assert, target }) {
const svg = target.querySelector('svg');
ok(svg);
assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg');
const text_elements = target.querySelectorAll('text');
assert.equal(text_elements.length, 5);
for (const { namespaceURI } of text_elements)
assert.equal(namespaceURI, 'http://www.w3.org/2000/svg');
}
});

@ -0,0 +1,7 @@
<script>
import Wrapper from "./Wrapper.svelte";
</script>
<svg>
<Wrapper />
</svg>
Loading…
Cancel
Save