Skip to content

Commit cd2263f

Browse files
navoritedummdidumm
andauthored
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 <[email protected]>
1 parent f5dc562 commit cd2263f

File tree

6 files changed

+116
-17
lines changed

6 files changed

+116
-17
lines changed

.changeset/dull-roses-relate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: infer `svg` namespace correctly

packages/svelte/src/compiler/phases/3-transform/utils.js

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -211,23 +211,67 @@ export function infer_namespace(namespace, parent, nodes, path) {
211211
parent_node.type === 'SvelteFragment' ||
212212
parent_node.type === 'SnippetBlock')
213213
) {
214-
// Heuristic: Keep current namespace, unless we find a regular element,
215-
// in which case we always want html, or we only find svg nodes,
216-
// in which case we assume svg.
217-
let only_svg = true;
218-
for (const node of nodes) {
219-
if (node.type === 'RegularElement') {
220-
if (!node.metadata.svg) {
221-
namespace = 'html';
222-
only_svg = false;
223-
break;
224-
}
225-
} else if (node.type !== 'Text' || node.data.trim() !== '') {
226-
only_svg = false;
227-
}
214+
const new_namespace = check_nodes_for_namespace(nodes, 'keep');
215+
if (new_namespace !== 'keep' && new_namespace !== 'maybe_html') {
216+
namespace = new_namespace;
228217
}
229-
if (only_svg) {
230-
namespace = 'svg';
218+
}
219+
220+
return namespace;
221+
}
222+
223+
/**
224+
* Heuristic: Keep current namespace, unless we find a regular element,
225+
* in which case we always want html, or we only find svg nodes,
226+
* in which case we assume svg.
227+
* @param {import('#compiler').SvelteNode[]} nodes
228+
* @param {import('#compiler').Namespace | 'keep' | 'maybe_html'} namespace
229+
*/
230+
function check_nodes_for_namespace(nodes, namespace) {
231+
for (const node of nodes) {
232+
if (node.type === 'RegularElement') {
233+
if (!node.metadata.svg) {
234+
namespace = 'html';
235+
break;
236+
} else if (namespace === 'keep') {
237+
namespace = 'svg';
238+
}
239+
} else if (
240+
(node.type === 'Text' && node.data.trim() !== '') ||
241+
node.type === 'HtmlTag' ||
242+
node.type === 'RenderTag'
243+
) {
244+
namespace = 'maybe_html';
245+
} else if (node.type === 'EachBlock') {
246+
namespace = check_nodes_for_namespace(node.body.nodes, namespace);
247+
if (namespace === 'html') break;
248+
if (node.fallback) {
249+
namespace = check_nodes_for_namespace(node.fallback.nodes, namespace);
250+
if (namespace === 'html') break;
251+
}
252+
} else if (node.type === 'IfBlock') {
253+
namespace = check_nodes_for_namespace(node.consequent.nodes, namespace);
254+
if (namespace === 'html') break;
255+
if (node.alternate) {
256+
namespace = check_nodes_for_namespace(node.alternate.nodes, namespace);
257+
if (namespace === 'html') break;
258+
}
259+
} else if (node.type === 'AwaitBlock') {
260+
if (node.pending) {
261+
namespace = check_nodes_for_namespace(node.pending.nodes, namespace);
262+
if (namespace === 'html') break;
263+
}
264+
if (node.then) {
265+
namespace = check_nodes_for_namespace(node.then.nodes, namespace);
266+
if (namespace === 'html') break;
267+
}
268+
if (node.catch) {
269+
namespace = check_nodes_for_namespace(node.catch.nodes, namespace);
270+
if (namespace === 'html') break;
271+
}
272+
} else if (node.type === 'KeyBlock') {
273+
namespace = check_nodes_for_namespace(node.fragment.nodes, namespace);
274+
if (namespace === 'html') break;
231275
}
232276
}
233277

packages/svelte/tests/runtime-runes/samples/destructure-async-assignments/main.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
return Promise.resolve([24, 25]);
3535
}
3636
37+
const some = {
38+
fn: () => {}
39+
}
40+
3741
const update = async () => {
3842
[a, b] = [1, await Promise.resolve(2)];
3943
({ c = await Promise.resolve(3), d } = { d: 4 });
@@ -49,7 +53,7 @@
4953
[l = obj[await Promise.resolve("prop")] + 1] = [];
5054
[m] = [`${1}${await Promise.resolve("3")}`];
5155
[n] = [-(await Promise.resolve(-14))];
52-
[o] = [(console.log(15), await Promise.resolve(15))];
56+
[o] = [(some.fn(), await Promise.resolve(15))];
5357
({ anotherprop: p = await Promise.resolve(16) } = obj);
5458
let val1, val2;
5559
({ val1 = (async function (x) { return await x; })(Promise.resolve(18)), r = await val1 }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<text x="0" y=14>outside</text>
2+
3+
{#if true}
4+
<text x="0" y="26">true</text>
5+
{:else}
6+
<text x="0" y="26">false</text>
7+
{/if}
8+
9+
{#each Array(3).fill(0) as item, idx}
10+
<text x={idx * 10} y={42}>{idx}</text>
11+
{/each}
12+
13+
<!-- comment should not set infer html namespace -->
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { test, ok } from '../../test';
2+
3+
export default test({
4+
html: `
5+
<svg>
6+
<text x="0" y="14">outside</text>
7+
<text x="0" y="26">true</text>
8+
<text x="0" y="42">0</text>
9+
<text x="10" y="42">1</text>
10+
<text x="20" y="42">2</text>
11+
</svg>
12+
`,
13+
test({ assert, target }) {
14+
const svg = target.querySelector('svg');
15+
ok(svg);
16+
17+
assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg');
18+
19+
const text_elements = target.querySelectorAll('text');
20+
21+
assert.equal(text_elements.length, 5);
22+
23+
for (const { namespaceURI } of text_elements)
24+
assert.equal(namespaceURI, 'http://www.w3.org/2000/svg');
25+
}
26+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
import Wrapper from "./Wrapper.svelte";
3+
</script>
4+
5+
<svg>
6+
<Wrapper />
7+
</svg>

0 commit comments

Comments
 (0)