Skip to content

Commit 90807ca

Browse files
fix: properly hydrate dynamic css props components and remove element removal (#16118)
1 parent 91272d7 commit 90807ca

File tree

8 files changed

+66
-30
lines changed

8 files changed

+66
-30
lines changed

.changeset/twelve-foxes-smell.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: properly hydrate dynamic css props components and remove element removal
Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,19 @@
1-
/** @import { Expression } from 'estree' */
21
/** @import { AST } from '#compiler' */
32
/** @import { ComponentContext } from '../types' */
4-
import * as b from '#compiler/builders';
3+
import { regex_is_valid_identifier } from '../../../patterns.js';
54
import { build_component } from './shared/component.js';
65

76
/**
87
* @param {AST.Component} node
98
* @param {ComponentContext} context
109
*/
1110
export function Component(node, context) {
12-
if (node.metadata.dynamic) {
13-
// Handle dynamic references to what seems like static inline components
14-
const component = build_component(node, '$$component', context, b.id('$$anchor'));
15-
context.state.init.push(
16-
b.stmt(
17-
b.call(
18-
'$.component',
19-
context.state.node,
20-
// TODO use untrack here to not update when binding changes?
21-
// Would align with Svelte 4 behavior, but it's arguably nicer/expected to update this
22-
b.thunk(/** @type {Expression} */ (context.visit(b.member_id(node.name)))),
23-
b.arrow([b.id('$$anchor'), b.id('$$component')], b.block([component]))
24-
)
25-
)
26-
);
27-
return;
28-
}
29-
30-
const component = build_component(node, node.name, context);
11+
const component = build_component(
12+
node,
13+
// if it's not dynamic we will just use the node name, if it is dynamic we will use the node name
14+
// only if it's a valid identifier, otherwise we will use a default name
15+
!node.metadata.dynamic || regex_is_valid_identifier.test(node.name) ? node.name : '$$component',
16+
context
17+
);
3118
context.state.init.push(component);
3219
}

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import { determine_slot } from '../../../../../utils/slot.js';
1313
* @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node
1414
* @param {string} component_name
1515
* @param {ComponentContext} context
16-
* @param {Expression} anchor
1716
* @returns {Statement}
1817
*/
19-
export function build_component(node, component_name, context, anchor = context.state.node) {
18+
export function build_component(node, component_name, context) {
19+
/**
20+
* @type {Expression}
21+
*/
22+
const anchor = context.state.node;
2023
/** @type {Array<Property[] | Expression>} */
2124
const props_and_spreads = [];
2225
/** @type {Array<() => void>} */
@@ -411,7 +414,7 @@ export function build_component(node, component_name, context, anchor = context.
411414
// TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components
412415
// will be handled separately through the `$.component` function, and then the component name will
413416
// always be referenced through just the identifier here.
414-
node.type === 'SvelteComponent'
417+
node.type === 'SvelteComponent' || (node.type === 'Component' && node.metadata.dynamic)
415418
? component_name
416419
: /** @type {Expression} */ (context.visit(b.member_id(component_name))),
417420
node_id,
@@ -429,14 +432,18 @@ export function build_component(node, component_name, context, anchor = context.
429432

430433
const statements = [...snippet_declarations];
431434

432-
if (node.type === 'SvelteComponent') {
435+
if (node.type === 'SvelteComponent' || (node.type === 'Component' && node.metadata.dynamic)) {
433436
const prev = fn;
434437

435438
fn = (node_id) => {
436439
return b.call(
437440
'$.component',
438441
node_id,
439-
b.thunk(/** @type {Expression} */ (context.visit(node.expression))),
442+
b.thunk(
443+
/** @type {Expression} */ (
444+
context.visit(node.type === 'Component' ? b.member_id(node.name) : node.expression)
445+
)
446+
),
440447
b.arrow(
441448
[b.id('$$anchor'), b.id(component_name)],
442449
b.block([...binding_initializers, b.stmt(prev(b.id('$$anchor')))])

packages/svelte/src/internal/client/dom/blocks/css-props.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,4 @@ export function css_props(element, get_styles) {
2626
}
2727
}
2828
});
29-
30-
teardown(() => {
31-
element.remove();
32-
});
3329
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div>a</div>
2+
3+
<style>
4+
div{
5+
color: var(--prop);
6+
}
7+
</style>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div>b</div>
2+
3+
<style>
4+
div{
5+
color: var(--prop);
6+
}
7+
</style>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { test } from '../../assert';
2+
import { flushSync } from 'svelte';
3+
4+
export default test({
5+
warnings: [],
6+
async test({ assert, target }) {
7+
const btn = target.querySelector('button');
8+
let div = /** @type {HTMLElement} */ (target.querySelector('div'));
9+
assert.equal(getComputedStyle(div).color, 'rgb(255, 0, 0)');
10+
flushSync(() => {
11+
btn?.click();
12+
});
13+
div = /** @type {HTMLElement} */ (target.querySelector('div'));
14+
assert.equal(getComputedStyle(div).color, 'rgb(255, 0, 0)');
15+
}
16+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import A from "./B.svelte";
3+
import B from "./A.svelte";
4+
let value = $state(0);
5+
6+
let Comp = $derived(value % 2 === 0 ? A : B);
7+
</script>
8+
9+
<button onclick={()=>value++}>click</button>
10+
11+
<Comp --prop="red"/>

0 commit comments

Comments
 (0)