Skip to content

Commit e1e7a75

Browse files
fix: allow async destructured deriveds (#16444)
* fix: allow async destructured deriveds * add test * tweak * tweak --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 307ec22 commit e1e7a75

File tree

6 files changed

+108
-15
lines changed

6 files changed

+108
-15
lines changed

.changeset/mighty-balloons-rush.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: allow async destructured deriveds

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

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,14 @@ export function VariableDeclaration(node, context) {
171171
context.state.transform[id.name] = { read: get_value };
172172

173173
const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
174-
const call = b.call('$.derived', expression);
175-
return b.declarator(
176-
id,
177-
dev ? b.call('$.tag', call, b.literal('[$state iterable]')) : call
178-
);
174+
let call = b.call('$.derived', expression);
175+
176+
if (dev) {
177+
const label = `[$state ${declarator.id.type === 'ArrayPattern' ? 'iterable' : 'object'}]`;
178+
call = b.call('$.tag', call, b.literal(label));
179+
}
180+
181+
return b.declarator(id, call);
179182
}),
180183
...paths.map((path) => {
181184
const value = /** @type {Expression} */ (context.visit(path.expression));
@@ -228,19 +231,37 @@ export function VariableDeclaration(node, context) {
228231
}
229232
} else {
230233
const init = /** @type {CallExpression} */ (declarator.init);
234+
let expression = /** @type {Expression} */ (
235+
context.visit(value, {
236+
...context.state,
237+
in_derived: rune === '$derived'
238+
})
239+
);
231240

232241
let rhs = value;
233242

234243
if (rune !== '$derived' || init.arguments[0].type !== 'Identifier') {
235244
const id = b.id(context.state.scope.generate('$$d'));
245+
let call = b.call('$.derived', rune === '$derived' ? b.thunk(expression) : expression);
246+
236247
rhs = b.call('$.get', id);
237248

238-
let expression = /** @type {Expression} */ (context.visit(value));
239-
if (rune === '$derived') expression = b.thunk(expression);
240-
const call = b.call('$.derived', expression);
241-
declarations.push(
242-
b.declarator(id, dev ? b.call('$.tag', call, b.literal('[$derived iterable]')) : call)
243-
);
249+
if (is_async) {
250+
const location = dev && !is_ignored(init, 'await_waterfall') && locate_node(init);
251+
call = b.call(
252+
'$.async_derived',
253+
b.thunk(expression, true),
254+
location ? b.literal(location) : undefined
255+
);
256+
call = b.call(b.await(b.call('$.save', call)));
257+
}
258+
259+
if (dev) {
260+
const label = `[$derived ${declarator.id.type === 'ArrayPattern' ? 'iterable' : 'object'}]`;
261+
call = b.call('$.tag', call, b.literal(label));
262+
}
263+
264+
declarations.push(b.declarator(id, call));
244265
}
245266

246267
const { inserts, paths } = extract_paths(declarator.id, rhs);
@@ -250,10 +271,14 @@ export function VariableDeclaration(node, context) {
250271
context.state.transform[id.name] = { read: get_value };
251272

252273
const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
253-
const call = b.call('$.derived', expression);
254-
declarations.push(
255-
b.declarator(id, dev ? b.call('$.tag', call, b.literal('[$derived iterable]')) : call)
256-
);
274+
let call = b.call('$.derived', expression);
275+
276+
if (dev) {
277+
const label = `[$derived ${declarator.id.type === 'ArrayPattern' ? 'iterable' : 'object'}]`;
278+
call = b.call('$.tag', call, b.literal(label));
279+
}
280+
281+
declarations.push(b.declarator(id, call));
257282
}
258283

259284
for (const path of paths) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
let count = $state(1);
3+
4+
let { squared, cubed } = $derived(await {
5+
squared: count ** 2,
6+
cubed: count ** 3
7+
});
8+
</script>
9+
10+
<button onclick={() => count++}>increment</button>
11+
12+
<p>{count} ** 2 = {squared}</p>
13+
<p>{count} ** 3 = {cubed}</p>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target }) {
6+
await tick();
7+
8+
const [increment] = target.querySelectorAll('button');
9+
10+
assert.htmlEqual(
11+
target.innerHTML,
12+
`
13+
<button>increment</button>
14+
<p>1 ** 2 = 1</p>
15+
<p>1 ** 3 = 1</p>
16+
`
17+
);
18+
19+
increment.click();
20+
await tick();
21+
22+
assert.htmlEqual(
23+
target.innerHTML,
24+
`
25+
<button>increment</button>
26+
<p>2 ** 2 = 4</p>
27+
<p>2 ** 3 = 8</p>
28+
`
29+
);
30+
}
31+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
</script>
4+
5+
<svelte:boundary>
6+
<Child />
7+
8+
{#snippet pending()}
9+
<p>pending</p>
10+
{/snippet}
11+
</svelte:boundary>

svelte.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// we need this so the VS Code extension doesn't yell at us
2+
export default {
3+
compilerOptions: {
4+
experimental: {
5+
async: true
6+
}
7+
}
8+
};

0 commit comments

Comments
 (0)