Skip to content

Commit f275aa3

Browse files
committed
Merge branch 'main' into branch-manager
2 parents d331f66 + bd2d3db commit f275aa3

File tree

28 files changed

+391
-35
lines changed

28 files changed

+391
-35
lines changed

.changeset/selfish-pets-teach.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:

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ Cyclical dependency detected: %cycle%
199199
### const_tag_invalid_reference
200200

201201
```
202-
The `{@const %name% = ...}` declaration is not available in this snippet
202+
The `{@const %name% = ...}` declaration is not available in this snippet
203203
```
204204

205205
The following is an error:
@@ -453,6 +453,12 @@ This turned out to be buggy and unpredictable, particularly when working with de
453453
{/each}
454454
```
455455

456+
### each_key_without_as
457+
458+
```
459+
An `{#each ...}` block without an `as` clause cannot have a key
460+
```
461+
456462
### effect_invalid_placement
457463

458464
```

packages/svelte/CHANGELOG.md

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

3+
## 5.41.0
4+
5+
### Minor Changes
6+
7+
- feat: add `$state.eager(value)` rune ([#16849](https://github.com/sveltejs/svelte/pull/16849))
8+
9+
### Patch Changes
10+
11+
- fix: preserve `<select>` state while focused ([#16958](https://github.com/sveltejs/svelte/pull/16958))
12+
13+
- chore: run boundary async effects in the context of the current batch ([#16968](https://github.com/sveltejs/svelte/pull/16968))
14+
15+
- fix: error if `each` block has `key` but no `as` clause ([#16966](https://github.com/sveltejs/svelte/pull/16966))
16+
317
## 5.40.2
418

519
### Patch Changes

packages/svelte/messages/compile-errors/template.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
127127
## const_tag_invalid_reference
128128

129-
> The `{@const %name% = ...}` declaration is not available in this snippet
129+
> The `{@const %name% = ...}` declaration is not available in this snippet
130130
131131
The following is an error:
132132

@@ -179,6 +179,10 @@ The same applies to components:
179179

180180
> `%type%` name cannot be empty
181181
182+
## each_key_without_as
183+
184+
> An `{#each ...}` block without an `as` clause cannot have a key
185+
182186
## element_invalid_closing_tag
183187

184188
> `</%name%>` attempted to close an element that was not open

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.0",
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/errors.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,13 +986,13 @@ export function const_tag_invalid_placement(node) {
986986
}
987987

988988
/**
989-
* The `{@const %name% = ...}` declaration is not available in this snippet
989+
* The `{@const %name% = ...}` declaration is not available in this snippet
990990
* @param {null | number | NodeLike} node
991991
* @param {string} name
992992
* @returns {never}
993993
*/
994994
export function const_tag_invalid_reference(node, name) {
995-
e(node, 'const_tag_invalid_reference', `The \`{@const ${name} = ...}\` declaration is not available in this snippet \nhttps://svelte.dev/e/const_tag_invalid_reference`);
995+
e(node, 'const_tag_invalid_reference', `The \`{@const ${name} = ...}\` declaration is not available in this snippet\nhttps://svelte.dev/e/const_tag_invalid_reference`);
996996
}
997997

998998
/**
@@ -1023,6 +1023,15 @@ export function directive_missing_name(node, type) {
10231023
e(node, 'directive_missing_name', `\`${type}\` name cannot be empty\nhttps://svelte.dev/e/directive_missing_name`);
10241024
}
10251025

1026+
/**
1027+
* An `{#each ...}` block without an `as` clause cannot have a key
1028+
* @param {null | number | NodeLike} node
1029+
* @returns {never}
1030+
*/
1031+
export function each_key_without_as(node) {
1032+
e(node, 'each_key_without_as', `An \`{#each ...}\` block without an \`as\` clause cannot have a key\nhttps://svelte.dev/e/each_key_without_as`);
1033+
}
1034+
10261035
/**
10271036
* `</%name%>` attempted to close an element that was not open
10281037
* @param {null | number | NodeLike} node

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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export function EachBlock(node, context) {
2828
node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index;
2929
}
3030

31+
if (node.metadata.keyed && !node.context) {
32+
e.each_key_without_as(node);
33+
}
34+
3135
// evaluate expression in parent scope
3236
context.visit(node.expression, {
3337
...context.state,

0 commit comments

Comments
 (0)