Skip to content

Commit

Permalink
fix: correctly infer <a> tag namespace (#14134)
Browse files Browse the repository at this point in the history
`<a>` tags are valid in both the SVG and HTML namespace.  If there's no parent, we therefore have to look downwards to see if it's the parent of a SVG or HTML element.

fixes #7807
fixes #13793
  • Loading branch information
dummdidumm authored Nov 3, 2024
1 parent 8de5605 commit 551284c
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/modern-pets-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: correctly infer `<a>` tag namespace
4 changes: 4 additions & 0 deletions packages/svelte/src/compiler/phases/2-analyze/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export interface AnalysisState {
analysis: ComponentAnalysis;
options: ValidatedCompileOptions;
ast_type: 'instance' | 'template' | 'module';
/**
* Tag name of the parent element. `null` if the parent is `svelte:element`, `#snippet`, a component or the root.
* Parent doesn't necessarily mean direct path predecessor because there could be `#each`, `#if` etc in-between.
*/
parent_element: string | null;
has_props_rune: boolean;
/** Which slots the current parent component has */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,17 @@ export function RegularElement(node, context) {
}

context.next({ ...context.state, parent_element: node.name });

// Special case: <a> tags are valid in both the SVG and HTML namespace.
// If there's no parent, look downwards to see if it's the parent of a SVG or HTML element.
if (node.name === 'a' && !context.state.parent_element) {
for (const child of node.fragment.nodes) {
if (child.type === 'RegularElement') {
if (child.metadata.svg && child.name !== 'svg') {
node.metadata.svg = true;
break;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @import { AST } from '#compiler' */
/** @import { Context } from '../../types' */
/** @import { AnalysisState, Context } from '../../types' */
import * as e from '../../../../errors.js';
import { get_attribute_expression, is_expression_attribute } from '../../../../utils/ast.js';
import { determine_slot } from '../../../../utils/slot.js';
Expand Down Expand Up @@ -96,6 +96,7 @@ export function visit_component(node, context) {
const component_slots = new Set();

for (const slot_name in nodes) {
/** @type {AnalysisState} */
const state = {
...context.state,
scope: node.metadata.scopes[slot_name],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { test } from '../../test';

export default test({
html: `
<div><a><span>Hello</span></a></div>
<div><a><span>Hello</span></a></div>
<div><a><span>Hello</span></a></div>
`,
test({ assert, target }) {
const div = target.querySelectorAll('div');
const a = target.querySelectorAll('a');
const span = target.querySelectorAll('span');

for (const element of div) {
assert.equal(element.namespaceURI, 'http://www.w3.org/1999/xhtml');
}

for (const element of a) {
assert.equal(element.namespaceURI, 'http://www.w3.org/1999/xhtml');
}

for (const element of span) {
assert.equal(element.namespaceURI, 'http://www.w3.org/1999/xhtml');
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
let { children } = $props();
</script>

<div>
{@render children()}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script>
import Div from './div.svelte';
</script>

<div>
<a>
<span>Hello</span>
</a>
</div>

<div>
{#snippet test()}
<a>
<span>Hello</span>
</a>
{/snippet}
{@render test()}
</div>

<Div>
<a>
<span>Hello</span>
</a>
</Div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { test } from '../../test';

export default test({
html: `
<svg><a><text>Hello</text></a></svg>
<svg><a><text>Hello</text></a></svg>
<svg><a><text>Hello</text></a></svg>
`,
test({ assert, target }) {
const svg = target.querySelectorAll('svg');
const a = target.querySelectorAll('a');
const text = target.querySelectorAll('text');

for (const element of svg) {
assert.equal(element.namespaceURI, 'http://www.w3.org/2000/svg');
}

for (const element of a) {
assert.equal(element.namespaceURI, 'http://www.w3.org/2000/svg');
}

for (const element of text) {
assert.equal(element.namespaceURI, 'http://www.w3.org/2000/svg');
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script>
import Svg from './svg.svelte';
</script>

<svg>
<a>
<text>Hello</text>
</a>
</svg>

<svg>
{#snippet test()}
<a>
<text>Hello</text>
</a>
{/snippet}
{@render test()}
</svg>

<Svg>
<a>
<text>Hello</text>
</a>
</Svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
let { children } = $props();
</script>

<svg>
{@render children()}
</svg>

0 comments on commit 551284c

Please sign in to comment.