Skip to content

Commit b04c5bb

Browse files
authored
fix: improve invalid nested interactive element error (#10199)
* fix: improve invalid nested interactive element error * add test * revise
1 parent db8cba3 commit b04c5bb

File tree

5 files changed

+48
-1
lines changed

5 files changed

+48
-1
lines changed

.changeset/giant-moons-own.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: improve invalid nested interactive element error

packages/svelte/src/compiler/phases/1-parse/utils/html.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,14 @@ function validate_code(code) {
122122
// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
123123

124124
// while `input` is also an interactive element, it is never moved by the browser, so we don't need to check for it
125-
const interactive_elements = new Set(['a', 'button', 'iframe', 'embed', 'select', 'textarea']);
125+
export const interactive_elements = new Set([
126+
'a',
127+
'button',
128+
'iframe',
129+
'embed',
130+
'select',
131+
'textarea'
132+
]);
126133

127134
/** @type {Record<string, Set<string>>} */
128135
const disallowed_contents = {

packages/svelte/src/compiler/phases/2-analyze/validation.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '../../utils/ast.js';
88
import { warn } from '../../warnings.js';
99
import fuzzymatch from '../1-parse/utils/fuzzymatch.js';
10+
import { interactive_elements } from '../1-parse/utils/html.js';
1011
import { binding_properties } from '../bindings.js';
1112
import { ContentEditableBindings, EventModifiers, SVGElements } from '../constants.js';
1213
import { is_custom_element_node } from '../nodes.js';
@@ -530,6 +531,19 @@ export const validation = {
530531
}
531532
}
532533

534+
if (interactive_elements.has(node.name)) {
535+
const path = context.path;
536+
for (let parent of path) {
537+
if (
538+
parent.type === 'RegularElement' &&
539+
parent.name === node.name &&
540+
interactive_elements.has(parent.name)
541+
) {
542+
error(node, 'invalid-node-placement', `<${node.name}>`, parent.name);
543+
}
544+
}
545+
}
546+
533547
context.next({
534548
...context.state,
535549
parent_element: node.name
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"code": "invalid-node-placement",
4+
"message": "<a> is invalid inside <a>",
5+
"start": {
6+
"line": 4,
7+
"column": 6
8+
},
9+
"end": {
10+
"line": 4,
11+
"column": 34
12+
}
13+
}
14+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div>
2+
<a href="/foo">
3+
<div>
4+
<p><a href="/foo">{`hello`}</a></p>
5+
</div>
6+
</a>
7+
</div>

0 commit comments

Comments
 (0)