Skip to content

Commit 7903a35

Browse files
committed
fix: add already connected validation
1 parent 91be79d commit 7903a35

File tree

6 files changed

+57
-1
lines changed

6 files changed

+57
-1
lines changed

documentation/docs/98-reference/.generated/client-errors.md

+6
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,9 @@ Reading state that was created inside the same derived is forbidden. Consider us
133133
```
134134
Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
135135
```
136+
137+
### svelte_element_already_connected
138+
139+
```
140+
You can't use an HTML element as the `this` attribute of `svelte:element` if it's already connected to a document
141+
```

packages/svelte/messages/client-errors/errors.md

+4
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,7 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
8787
## state_unsafe_mutation
8888

8989
> Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
90+
91+
## svelte_element_already_connected
92+
93+
> You can't use an HTML element as the `this` attribute of `svelte:element` if it's already connected to a document

packages/svelte/src/internal/client/dom/blocks/svelte-element.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { DEV } from 'esm-env';
2323
import { EFFECT_TRANSPARENT } from '../../constants.js';
2424
import { assign_nodes } from '../template.js';
2525
import { is_raw_text_element } from '../../../../utils.js';
26+
import * as e from '../../errors.js';
2627

2728
/**
2829
* @param {Comment | Element} node
@@ -70,11 +71,19 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
7071

7172
block(() => {
7273
const next_tag = get_tag() || null;
73-
var ns = get_namespace ? get_namespace() : is_svg || next_tag === 'svg' ? NAMESPACE_SVG : null;
74+
var ns = get_namespace
75+
? get_namespace()
76+
: is_svg || (typeof next_tag === 'string' ? next_tag === 'svg' : next_tag?.tagName === 'svg')
77+
? NAMESPACE_SVG
78+
: null;
7479

7580
// Assumption: Noone changes the namespace but not the tag (what would that even mean?)
7681
if (next_tag === tag) return;
7782

83+
if (typeof next_tag !== 'string' && next_tag?.isConnected) {
84+
e.svelte_element_already_connected();
85+
}
86+
7887
// See explanation of `each_item_block` above
7988
var previous_each_item = current_each_item;
8089
set_current_each_item(each_item_block);

packages/svelte/src/internal/client/errors.js

+15
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,19 @@ export function state_unsafe_mutation() {
335335
} else {
336336
throw new Error(`https://svelte.dev/e/state_unsafe_mutation`);
337337
}
338+
}
339+
340+
/**
341+
* You can't use an HTML element as the `this` attribute of `svelte:element` if it's already connected to a document
342+
* @returns {never}
343+
*/
344+
export function svelte_element_already_connected() {
345+
if (DEV) {
346+
const error = new Error(`svelte_element_already_connected\nYou can't use an HTML element as the \`this\` attribute of \`svelte:element\` if it's already connected to a document\nhttps://svelte.dev/e/svelte_element_already_connected`);
347+
348+
error.name = 'Svelte error';
349+
throw error;
350+
} else {
351+
throw new Error(`https://svelte.dev/e/svelte_element_already_connected`);
352+
}
338353
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
test({ assert, target }) {
6+
const btn = target.querySelector('button');
7+
assert.throws(() => {
8+
flushSync(() => {
9+
btn?.click();
10+
});
11+
}, 'svelte_element_already_connected');
12+
}
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
let div = $state(null);
3+
let show = $state(false);
4+
</script>
5+
6+
<div bind:this={div}></div>
7+
<button onclick={() => show = !show}></button>
8+
<svelte:element this={show && div}>
9+
</svelte:element>

0 commit comments

Comments
 (0)