await dependencies of style directives

bindings-async-fix
Simon Holthausen 1 day ago
parent a60fffc456
commit eb0845e365

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: await dependencies of style directives

@ -23,6 +23,9 @@ export function StyleDirective(node, context) {
if (binding.kind !== 'normal') {
node.metadata.expression.has_state = true;
}
if (binding.blocker) {
node.metadata.expression.dependencies.add(binding);
}
}
} else {
context.next();
@ -30,9 +33,7 @@ export function StyleDirective(node, context) {
for (const chunk of get_attribute_chunks(node.value)) {
if (chunk.type !== 'ExpressionTag') continue;
node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
node.metadata.expression.has_await ||= chunk.metadata.expression.has_await;
node.metadata.expression.merge(chunk.metadata.expression);
}
}
}

@ -484,21 +484,6 @@ function setup_select_synchronization(value_binding, context) {
);
}
/**
* @param {ExpressionMetadata} target
* @param {ExpressionMetadata} source
*/
function merge_metadata(target, source) {
target.has_assignment ||= source.has_assignment;
target.has_await ||= source.has_await;
target.has_call ||= source.has_call;
target.has_member_expression ||= source.has_member_expression;
target.has_state ||= source.has_state;
for (const r of source.references) target.references.add(r);
for (const b of source.dependencies) target.dependencies.add(b);
}
/**
* @param {AST.ClassDirective[]} class_directives
* @param {ComponentContext} context
@ -514,7 +499,7 @@ export function build_class_directives_object(
const metadata = new ExpressionMetadata();
for (const d of class_directives) {
merge_metadata(metadata, d.metadata.expression);
metadata.merge(d.metadata.expression);
const expression = /** @type Expression */ (context.visit(d.expression));
properties.push(b.init(d.name, expression));
@ -541,7 +526,7 @@ export function build_style_directives_object(
const metadata = new ExpressionMetadata();
for (const d of style_directives) {
merge_metadata(metadata, d.metadata.expression);
metadata.merge(d.metadata.expression);
const expression =
d.value === true

@ -170,7 +170,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c
if (class_directives.length) {
next = build_class_directives_object(class_directives, context);
has_state ||= class_directives.some(
(d) => d.metadata.expression.has_state || d.metadata.expression.has_await
(d) => d.metadata.expression.has_state || d.metadata.expression.is_async()
);
if (has_state) {
@ -240,7 +240,7 @@ export function build_set_style(node_id, attribute, style_directives, context) {
if (style_directives.length) {
next = build_style_directives_object(style_directives, context);
has_state ||= style_directives.some(
(d) => d.metadata.expression.has_state || d.metadata.expression.has_await
(d) => d.metadata.expression.has_state || d.metadata.expression.is_async()
);
if (has_state) {

@ -115,6 +115,21 @@ export class ExpressionMetadata {
is_async() {
return this.has_await || this.#get_blockers().size > 0;
}
/**
* @param {ExpressionMetadata} source
*/
merge(source) {
this.has_state ||= source.has_state;
this.has_call ||= source.has_call;
this.has_await ||= source.has_await;
this.has_member_expression ||= source.has_member_expression;
this.has_assignment ||= source.has_assignment;
this.#blockers = null; // so that blockers are recalculated
for (const r of source.references) this.references.add(r);
for (const b of source.dependencies) this.dependencies.add(b);
}
}
/**

@ -0,0 +1,30 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['async-server', 'client', 'hydrate'],
ssrHtml: `
<div style="color: red;"></div>
<div style="width: 100px;"></div>
<button>width</button>
<div style="color: red;"></div>
<div style="width: 100px;"></div>
<button>width</button>
`,
async test({ assert, target }) {
await tick();
assert.htmlEqual(
target.innerHTML,
`
<div style="color: red;"></div>
<div style="width: 100px;"></div>
<button>width</button>
<div style="color: red;"></div>
<div style="width: 100px;"></div>
<button>width</button>
`
);
}
});

@ -0,0 +1,25 @@
<script>
await Promise.resolve();
// static
let color = $state('red');
// dynamic
let width = $state('100px');
</script>
<!-- force them into their own template effects -->
<!-- normal -->
{#if color}
<div style:color={color}></div>
<div style:width={width}></div>
<button onclick={() => width = '1px'}>width</button>
{/if}
<!-- shorthand -->
{#if color}
<div style:color></div>
<div style:width></div>
<button onclick={() => width = '1px'}>width</button>
{/if}
Loading…
Cancel
Save