Skip to content

Commit 891b54f

Browse files
authored
Merge branch 'main' into push-zkpzuqxyyknn
2 parents 749a056 + 7d9ea8e commit 891b54f

File tree

40 files changed

+652
-508
lines changed

40 files changed

+652
-508
lines changed

.changeset/quiet-weeks-camp.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/selfish-pets-teach.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/thirty-rules-dance.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

documentation/docs/02-runes/02-$state.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snaps
166166

167167
This is handy when you want to pass some state to an external library or API that doesn't expect a proxy, such as `structuredClone`.
168168

169+
## `$state.eager`
170+
171+
When state changes, it may not be reflected in the UI immediately if it is used by an `await` expression, because [updates are synchronized](await-expressions#Synchronized-updates).
172+
173+
In some cases, you may want to update the UI as soon as the state changes. For example, you might want to update a navigation bar when the user clicks on a link, so that they get visual feedback while waiting for the new page to load. To do this, use `$state.eager(value)`:
174+
175+
```svelte
176+
<nav>
177+
<a href="/" aria-current={$state.eager(pathname) === '/' ? 'page' : null}>home</a>
178+
<a href="/about" aria-current={$state.eager(pathname) === '/about' ? 'page' : null}>about</a>
179+
</nav>
180+
```
181+
182+
Use this feature sparingly, and only to provide feedback in response to user action — in general, allowing Svelte to coordinate updates will provide a better user experience.
183+
169184
## Passing state into functions
170185

171186
JavaScript is a _pass-by-value_ language — when you call a function, the arguments are the _values_ rather than the _variables_. In other words:

packages/svelte/CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# svelte
22

3+
## 5.41.1
4+
5+
### Patch Changes
6+
7+
- fix: place `let:` declarations before `{@const}` declarations ([#16985](https://github.com/sveltejs/svelte/pull/16985))
8+
9+
- fix: improve `each_key_without_as` error ([#16983](https://github.com/sveltejs/svelte/pull/16983))
10+
11+
- chore: centralise branch management ([#16977](https://github.com/sveltejs/svelte/pull/16977))
12+
13+
## 5.41.0
14+
15+
### Minor Changes
16+
17+
- feat: add `$state.eager(value)` rune ([#16849](https://github.com/sveltejs/svelte/pull/16849))
18+
19+
### Patch Changes
20+
21+
- fix: preserve `<select>` state while focused ([#16958](https://github.com/sveltejs/svelte/pull/16958))
22+
23+
- chore: run boundary async effects in the context of the current batch ([#16968](https://github.com/sveltejs/svelte/pull/16968))
24+
25+
- fix: error if `each` block has `key` but no `as` clause ([#16966](https://github.com/sveltejs/svelte/pull/16966))
26+
327
## 5.40.2
428

529
### Patch Changes

packages/svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.40.2",
5+
"version": "5.41.1",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/src/ambient.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ declare namespace $state {
9595
: never
9696
: never;
9797

98+
/**
99+
* Returns the latest `value`, even if the rest of the UI is suspending
100+
* while async work (such as data loading) completes.
101+
*
102+
* ```svelte
103+
* <nav>
104+
* <a href="/" aria-current={$state.eager(pathname) === '/' ? 'page' : null}>home</a>
105+
* <a href="/about" aria-current={$state.eager(pathname) === '/about' ? 'page' : null}>about</a>
106+
* </nav>
107+
* ```
108+
*/
109+
export function eager<T>(value: T): T;
98110
/**
99111
* Declares state that is _not_ made deeply reactive — instead of mutating it,
100112
* you must reassign it.

packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ export function CallExpression(node, context) {
226226
break;
227227
}
228228

229+
case '$state.eager':
230+
if (node.arguments.length !== 1) {
231+
e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
232+
}
233+
234+
break;
235+
229236
case '$state.snapshot':
230237
if (node.arguments.length !== 1) {
231238
e.rune_invalid_arguments_length(node, rune, 'exactly one argument');

packages/svelte/src/compiler/phases/2-analyze/visitors/EachBlock.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/** @import { Expression } from 'estree' */
12
/** @import { AST, Binding } from '#compiler' */
23
/** @import { Context } from '../types' */
34
/** @import { Scope } from '../../scope' */
@@ -29,7 +30,7 @@ export function EachBlock(node, context) {
2930
}
3031

3132
if (node.metadata.keyed && !node.context) {
32-
e.each_key_without_as(node);
33+
e.each_key_without_as(/** @type {Expression} */ (node.key));
3334
}
3435

3536
// evaluate expression in parent scope

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export function client_component(analysis, options) {
172172
// these are set inside the `Fragment` visitor, and cannot be used until then
173173
init: /** @type {any} */ (null),
174174
consts: /** @type {any} */ (null),
175+
let_directives: /** @type {any} */ (null),
175176
update: /** @type {any} */ (null),
176177
after_update: /** @type {any} */ (null),
177178
template: /** @type {any} */ (null),
@@ -384,7 +385,9 @@ export function client_component(analysis, options) {
384385
.../** @type {ESTree.Statement[]} */ (template.body)
385386
]);
386387

387-
component_block.body.push(b.stmt(b.call(`$.async_body`, b.arrow([], body, true))));
388+
component_block.body.push(
389+
b.stmt(b.call(`$.async_body`, b.id('$$anchor'), b.arrow([b.id('$$anchor')], body, true)))
390+
);
388391
} else {
389392
component_block.body.push(
390393
...state.instance_level_snippets,

0 commit comments

Comments
 (0)