Skip to content

Commit c0ade24

Browse files
committed
merge main
2 parents f686128 + fcfbc9c commit c0ade24

File tree

421 files changed

+11682
-2986
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

421 files changed

+11682
-2986
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ jobs:
4343
- run: pnpm test
4444
env:
4545
CI: true
46+
TestNoAsync:
47+
permissions: {}
48+
runs-on: ubuntu-latest
49+
timeout-minutes: 10
50+
steps:
51+
- uses: actions/checkout@v4
52+
- uses: pnpm/action-setup@v4
53+
- uses: actions/setup-node@v4
54+
with:
55+
node-version: 22
56+
cache: pnpm
57+
- run: pnpm install --frozen-lockfile
58+
- run: pnpm playwright install chromium
59+
- run: pnpm test runtime-runes
60+
env:
61+
CI: true
62+
SVELTE_NO_ASYNC: true
4663
Lint:
4764
permissions: {}
4865
runs-on: ubuntu-latest

.github/workflows/ecosystem-ci-trigger.yml

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,17 @@ jobs:
88
trigger:
99
runs-on: ubuntu-latest
1010
if: github.repository == 'sveltejs/svelte' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
11+
permissions:
12+
issues: write # to add / delete reactions
13+
pull-requests: write # to read PR data, and to add labels
14+
actions: read # to check workflow status
15+
contents: read # to clone the repo
1116
steps:
12-
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
13-
- uses: actions/github-script@v6
17+
- name: monitor action permissions
18+
uses: GitHubSecurityLab/actions-permissions/monitor@v1
19+
- name: check user authorization # user needs triage permission
20+
uses: actions/github-script@v7
21+
id: check-permissions
1422
with:
1523
script: |
1624
const user = context.payload.sender.login
@@ -29,24 +37,26 @@ jobs:
2937
}
3038
3139
if (hasTriagePermission) {
32-
console.log('Allowed')
40+
console.log('User is allowed. Adding +1 reaction.')
3341
await github.rest.reactions.createForIssueComment({
3442
owner: context.repo.owner,
3543
repo: context.repo.repo,
3644
comment_id: context.payload.comment.id,
3745
content: '+1',
3846
})
3947
} else {
40-
console.log('Not allowed')
48+
console.log('User is not allowed. Adding -1 reaction.')
4149
await github.rest.reactions.createForIssueComment({
4250
owner: context.repo.owner,
4351
repo: context.repo.repo,
4452
comment_id: context.payload.comment.id,
4553
content: '-1',
4654
})
47-
throw new Error('not allowed')
55+
throw new Error('User does not have the necessary permissions.')
4856
}
49-
- uses: actions/github-script@v6
57+
58+
- name: Get PR Data
59+
uses: actions/github-script@v7
5060
id: get-pr-data
5161
with:
5262
script: |
@@ -59,21 +69,27 @@ jobs:
5969
return {
6070
num: context.issue.number,
6171
branchName: pr.head.ref,
72+
commit: pr.head.sha,
6273
repo: pr.head.repo.full_name
6374
}
64-
- id: generate-token
65-
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 #keep pinned for security reasons, currently 1.8.0
75+
76+
- name: Generate Token
77+
id: generate-token
78+
uses: actions/create-github-app-token@v2
6679
with:
67-
app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }}
68-
private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
69-
repository: '${{ github.repository_owner }}/svelte-ecosystem-ci'
70-
- uses: actions/github-script@v6
80+
app-id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }}
81+
private-key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
82+
repositories: |
83+
svelte
84+
svelte-ecosystem-ci
85+
86+
- name: Trigger Downstream Workflow
87+
uses: actions/github-script@v7
7188
id: trigger
7289
env:
7390
COMMENT: ${{ github.event.comment.body }}
7491
with:
7592
github-token: ${{ steps.generate-token.outputs.token }}
76-
result-encoding: string
7793
script: |
7894
const comment = process.env.COMMENT.trim()
7995
const prData = ${{ steps.get-pr-data.outputs.result }}
@@ -89,6 +105,7 @@ jobs:
89105
prNumber: '' + prData.num,
90106
branchName: prData.branchName,
91107
repo: prData.repo,
108+
commit: prData.commit,
92109
suite: suite === '' ? '-' : suite
93110
}
94111
})

.prettierignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ packages/**/config/*.js
77

88
# packages/svelte
99
packages/svelte/messages/**/*.md
10+
packages/svelte/scripts/_bundle.js
1011
packages/svelte/src/compiler/errors.js
1112
packages/svelte/src/compiler/warnings.js
1213
packages/svelte/src/internal/client/errors.js
@@ -25,8 +26,7 @@ packages/svelte/tests/hydration/samples/*/_expected.html
2526
packages/svelte/tests/hydration/samples/*/_override.html
2627
packages/svelte/types
2728
packages/svelte/compiler/index.js
28-
playgrounds/sandbox/input/**.svelte
29-
playgrounds/sandbox/output
29+
playgrounds/sandbox/src/*
3030

3131
# sites/svelte.dev
3232
sites/svelte.dev/static/svelte-app.json

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ Test samples are kept in `/test/xxx/samples` folder.
105105
pnpm test validator
106106
```
107107

108-
1. To filter tests _within_ a test suite, use `pnpm test <suite-name> -- -t <test-name>`, for example:
108+
1. To filter tests _within_ a test suite, use `pnpm test <suite-name> -t <test-name>`, for example:
109109

110110
```bash
111-
pnpm test validator -- -t a11y-alt-text
111+
pnpm test validator -t a11y-alt-text
112112
```
113113

114114
(You can also do `FILTER=<test-name> pnpm test <suite-name>` which removes other tests rather than simply skipping them — this will result in faster and more compact test results, but it's non-idiomatic. Choose your fighter.)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ todos.push({
5050
});
5151
```
5252

53-
> [!NOTE] When you update properties of proxies, the original object is _not_ mutated.
53+
> [!NOTE] When you update properties of proxies, the original object is _not_ mutated. If you need to use your own proxy handlers in a state proxy, [you should wrap the object _after_ wrapping it in `$state`](https://svelte.dev/playground/hello-world?version=latest#H4sIAAAAAAAACpWR3WoDIRCFX2UqhWyIJL3erAulL9C7XnQLMe5ksbUqOpsfln33YuyGFNJC8UKdc2bOhw7Myk9kJXsJ0nttO9jcR5KEG9AWJDwHdzwxznbaYGTl68Do5JM_FRifuh-9X8Y9Gkq1rYx4q66cJbQUWcmqqIL2VDe2IYMEbvuOikBADi-GJDSkXG-phId0G-frye2DO2psQYDFQ0Ys8gQO350dUkEydEg82T0GOs0nsSG9g2IqgxACZueo2ZUlpdvoDC6N64qsg1QKY8T2bpZp8gpIfbCQ85Zn50Ud82HkeY83uDjspenxv3jXcSDyjPWf9L1vJf0GH666J-jLu1ery4dV257IWXBWGa0-xFDMQdTTn2ScxWKsn86ROsLwQxqrVR5QM84Ij8TKFD2-cUZSm4O2LSt30kQcvwCgCmfZnAIAAA==).
5454
5555
Note that if you destructure a reactive value, the references are not reactive — as in normal JavaScript, they are evaluated at the point of destructuring:
5656

@@ -119,7 +119,7 @@ class Todo {
119119
}
120120
```
121121

122-
> Svelte provides reactive implementations of built-in classes like `Set` and `Map` that can be imported from [`svelte/reactivity`](svelte-reactivity).
122+
> [NOTE!] Svelte provides reactive implementations of built-in classes like `Set` and `Map` that can be imported from [`svelte/reactivity`](svelte-reactivity).
123123
124124
## `$state.raw`
125125

documentation/docs/02-runes/03-$derived.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,23 @@ let selected = $derived(items[index]);
9494

9595
...you can change (or `bind:` to) properties of `selected` and it will affect the underlying `items` array. If `items` was _not_ deeply reactive, mutating `selected` would have no effect.
9696

97+
## Destructuring
98+
99+
If you use destructuring with a `$derived` declaration, the resulting variables will all be reactive — this...
100+
101+
```js
102+
let { a, b, c } = $derived(stuff());
103+
```
104+
105+
...is roughly equivalent to this:
106+
107+
```js
108+
let _stuff = $derived(stuff());
109+
let a = $derived(_stuff.a);
110+
let b = $derived(_stuff.b);
111+
let c = $derived(_stuff.c);
112+
```
113+
97114
## Update propagation
98115

99116
Svelte uses something called _push-pull reactivity_ — when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull').

documentation/docs/02-runes/04-$effect.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,21 @@ The `$effect.tracking` rune is an advanced feature that tells you whether or not
221221

222222
It is used to implement abstractions like [`createSubscriber`](/docs/svelte/svelte-reactivity#createSubscriber), which will create listeners to update reactive values but _only_ if those values are being tracked (rather than, for example, read inside an event handler).
223223

224+
## `$effect.pending`
225+
226+
When using [`await`](await-expressions) in components, the `$effect.pending()` rune tells you how many promises are pending in the current [boundary](svelte-boundary), not including child boundaries ([demo](/playground/untitled#H4sIAAAAAAAAE3WRMU_DMBCF_8rJdHDUqilILGkaiY2RgY0yOPYZWbiOFV8IleX_jpMUEAIWS_7u-d27c2ROnJBV7B6t7WDsequAozKEqmAbpo3FwKqnyOjsJ90EMr-8uvN-G97Q0sRaEfAvLjtH6CjbsDrI3nhqju5IFgkEHGAVSBDy62L_SdtvejPTzEU4Owl6cJJM50AoxcUG2gLiVM31URgChyM89N3JBORcF3BoICA9mhN2A3G9gdvdrij2UJYgejLaSCMsKLTivNj0SEOf7WEN7ZwnHV1dfqd2dTsQ5QCdk9bI10PkcxexXqcmH3W51Jt_le2kbH8os9Y3UaTcNLYpDx-Xab6GTHXpZ128MhpWqDVK2np0yrgXXqQpaLa4APDLBkIF8bd2sYql0Sn_DeE7sYr6AdNzvgljR-MUq7SwAdMHeUtgHR4CAAA=)):
227+
228+
```svelte
229+
<button onclick={() => a++}>a++</button>
230+
<button onclick={() => b++}>b++</button>
231+
232+
<p>{a} + {b} = {await add(a, b)}</p>
233+
234+
{#if $effect.pending()}
235+
<p>pending promises: {$effect.pending()}</p>
236+
{/if}
237+
```
238+
224239
## `$effect.root`
225240

226241
The `$effect.root` rune is an advanced feature that creates a non-tracked scope that doesn't auto-cleanup. This is useful for nested effects that you want to manually control. This rune also allows for the creation of effects outside of the component initialisation phase.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
title: await
3+
---
4+
5+
As of Svelte 5.36, you can use the `await` keyword inside your components in three places where it was previously unavailable:
6+
7+
- at the top level of your component's `<script>`
8+
- inside `$derived(...)` declarations
9+
- inside your markup
10+
11+
This feature is currently experimental, and you must opt in by adding the `experimental.async` option wherever you [configure](/docs/kit/configuration) Svelte, usually `svelte.config.js`:
12+
13+
```js
14+
/// file: svelte.config.js
15+
export default {
16+
compilerOptions: {
17+
experimental: {
18+
async: true
19+
}
20+
}
21+
};
22+
```
23+
24+
The experimental flag will be removed in Svelte 6.
25+
26+
## Boundaries
27+
28+
Currently, you can only use `await` inside a [`<svelte:boundary>`](svelte-boundary) with a `pending` snippet:
29+
30+
```svelte
31+
<svelte:boundary>
32+
<MyApp />
33+
34+
{#snippet pending()}
35+
<p>loading...</p>
36+
{/snippet}
37+
</svelte:boundary>
38+
```
39+
40+
This restriction will be lifted once Svelte supports asynchronous server-side rendering (see [caveats](#Caveats)).
41+
42+
> [!NOTE] In the [playground](/playground), your app is rendered inside a boundary with an empty pending snippet, so that you can use `await` without having to create one.
43+
44+
## Synchronized updates
45+
46+
When an `await` expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like [this](/playground/untitled#H4sIAAAAAAAAE42QsWrDQBBEf2VZUkhYRE4gjSwJ0qVMkS6XYk9awcFpJe5Wdoy4fw-ycdykSPt2dpiZFYVGxgrf2PsJTlPwPWTcO-U-xwIH5zli9bminudNtwEsbl-v8_wYj-x1Y5Yi_8W7SZRFI1ZYxy64WVsjRj0rEDTwEJWUs6f8cKP2Tp8vVIxSPEsHwyKdukmA-j6jAmwO63Y1SidyCsIneA_T6CJn2ZBD00Jk_XAjT4tmQwEv-32eH6AsgYK6wXWOPPTs6Xy1CaxLECDYgb3kSUbq8p5aaifzorCt0RiUZbQcDIJ10ldH8gs3K6X2Xzqbro5zu1KCHaw2QQPrtclvwVSXc2sEC1T-Vqw0LJy-ClRy_uSkx2ogHzn9ADZ1CubKAQAA)...
47+
48+
```svelte
49+
<script>
50+
let a = $state(1);
51+
let b = $state(2);
52+
53+
async function add(a, b) {
54+
await new Promise((f) => setTimeout(f, 500)); // artificial delay
55+
return a + b;
56+
}
57+
</script>
58+
59+
<input type="number" bind:value={a}>
60+
<input type="number" bind:value={b}>
61+
62+
<p>{a} + {b} = {await add(a, b)}</p>
63+
```
64+
65+
...if you increment `a`, the contents of the `<p>` will _not_ immediately update to read this —
66+
67+
```html
68+
<p>2 + 2 = 3</p>
69+
```
70+
71+
— instead, the text will update to `2 + 2 = 4` when `add(a, b)` resolves.
72+
73+
Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.
74+
75+
## Concurrency
76+
77+
Svelte will do as much asynchronous work as it can in parallel. For example if you have two `await` expressions in your markup...
78+
79+
```svelte
80+
<p>{await one()}</p>
81+
<p>{await two()}</p>
82+
```
83+
84+
...both functions will run at the same time, as they are independent expressions, even though they are _visually_ sequential.
85+
86+
This does not apply to sequential `await` expressions inside your `<script>` or inside async functions — these run like any other asynchronous JavaScript. An exception is that independent `$derived` expressions will update independently, even though they will run sequentially when they are first created:
87+
88+
```js
89+
async function one() { return 1; }
90+
async function two() { return 2; }
91+
// ---cut---
92+
// these will run sequentially the first time,
93+
// but will update independently
94+
let a = $derived(await one());
95+
let b = $derived(await two());
96+
```
97+
98+
> [!NOTE] If you write code like this, expect Svelte to give you an [`await_waterfall`](runtime-warnings#Client-warnings-await_waterfall) warning
99+
100+
## Indicating loading states
101+
102+
In addition to the nearest boundary's [`pending`](svelte-boundary#Properties-pending) snippet, you can indicate that asynchronous work is ongoing with [`$effect.pending()`]($effect#$effect.pending).
103+
104+
You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete:
105+
106+
```js
107+
let color = 'red';
108+
let answer = -1;
109+
let updating = false;
110+
// ---cut---
111+
import { tick, settled } from 'svelte';
112+
113+
async function onclick() {
114+
updating = true;
115+
116+
// without this, the change to `updating` will be
117+
// grouped with the other changes, meaning it
118+
// won't be reflected in the UI
119+
await tick();
120+
121+
color = 'octarine';
122+
answer = 42;
123+
124+
await settled();
125+
126+
// any updates affected by `color` or `answer`
127+
// have now been applied
128+
updating = false;
129+
}
130+
```
131+
132+
## Error handling
133+
134+
Errors in `await` expressions will bubble to the nearest [error boundary](svelte-boundary).
135+
136+
## Caveats
137+
138+
As an experimental feature, the details of how `await` is handled (and related APIs like `$effect.pending()`) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum.
139+
140+
Currently, server-side rendering is synchronous. If a `<svelte:boundary>` with a `pending` snippet is encountered during SSR, only the `pending` snippet will be rendered.
141+
142+
## Breaking changes
143+
144+
Effects run in a slightly different order when the `experimental.async` option is `true`. Specifically, _block_ effects like `{#if ...}` and `{#each ...}` now run before an `$effect.pre` or `beforeUpdate` in the same component, which means that in [very rare situations](/playground/untitled?#H4sIAAAAAAAAE22R3VLDIBCFX2WLvUhnTHsf0zre-Q7WmfwtFV2BgU1rJ5N3F0jaOuoVcPbw7VkYhK4_URTiGYkMnIyjDjLsFGO3EvdCKkIvipdB8NlGXxSCPt96snbtj0gctab2-J_eGs2oOWBE6VunLO_2es-EDKZ5x5ZhC0vPNWM2gHXGouNzAex6hHH1cPHil_Lsb95YT9VQX6KUAbS2DrNsBdsdDFHe8_XSYjH1SrhELTe3MLpsemajweiWVPuxHSbKNd-8eQTdE0EBf4OOaSg2hwNhhE_ABB_ulJzjj9FULvIcqgm5vnAqUB7wWFMfhuugQWkcAr8hVD-mq8D12kOep24J_IszToOXdveGDsuNnZwbJUNlXsKnhJdhUcTo42s41YpOSneikDV5HL8BktM6yRcCAAA=) it is possible to update a block that should no longer exist, but only if you update state inside an effect, [which you should avoid]($effect#When-not-to-use-$effect).

0 commit comments

Comments
 (0)