Skip to content

feat: allow await in components #15844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 533 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
533 commits
Select commit Hold shift + click to select a range
4e417e1
fix
Rich-Harris Feb 26, 2025
f90132c
fix
Rich-Harris Feb 26, 2025
ac33857
fix
Rich-Harris Feb 26, 2025
9b36b6b
add callsite to effect tree logs
Rich-Harris Feb 27, 2025
3c350db
fix
Rich-Harris Feb 27, 2025
8a96f23
tidy
Rich-Harris Feb 27, 2025
eb8c8e6
simplify
Rich-Harris Feb 27, 2025
52d4ade
simplify
Rich-Harris Feb 27, 2025
47a1693
fix
Rich-Harris Feb 27, 2025
94da28f
skip test
Rich-Harris Feb 27, 2025
ee71311
fix
Rich-Harris Feb 28, 2025
a0a4d4f
fix
Rich-Harris Feb 28, 2025
31882d1
add `$effect.pending()`
Rich-Harris Feb 28, 2025
49480f0
try this
Rich-Harris Feb 28, 2025
5bcdb13
fix
Rich-Harris Feb 28, 2025
3d970d2
merge main -> async
Rich-Harris Apr 16, 2025
3decb67
add TODO
Rich-Harris Apr 16, 2025
8ba4f71
Merge branch 'main' into async
Rich-Harris Apr 16, 2025
90cdc16
align with main
Rich-Harris Apr 16, 2025
02efac9
fix
Rich-Harris Apr 16, 2025
d5922f8
merge main
Rich-Harris Apr 16, 2025
fcbb54d
merge main
Rich-Harris Apr 17, 2025
888fc31
is_async -> has_await
Rich-Harris Apr 17, 2025
4b7130e
merge main
Rich-Harris Apr 17, 2025
94a4b11
Merge branch 'main' into async
Rich-Harris Apr 18, 2025
ab0ec6f
don't update a focused input (may need to add a blur handler later, w…
Rich-Harris Apr 18, 2025
cc2b888
merge main
Rich-Harris Apr 18, 2025
521b228
docs
Rich-Harris Apr 18, 2025
037e289
fix
Rich-Harris Apr 18, 2025
6688eb8
remove indirection
Rich-Harris Apr 18, 2025
cb2f68e
QOL
Rich-Harris Apr 18, 2025
4f45033
move stuff
Rich-Harris Apr 18, 2025
a469c39
update test to not rely on props
Rich-Harris Apr 19, 2025
43457cc
.
Rich-Harris Apr 19, 2025
8b691e9
rename
Rich-Harris Apr 19, 2025
81f066f
update test
Rich-Harris Apr 19, 2025
a840f00
tweak
Rich-Harris Apr 19, 2025
3fe77cd
tweak
Rich-Harris Apr 19, 2025
b7c3995
tweak
Rich-Harris Apr 19, 2025
2620a21
tweak
Rich-Harris Apr 19, 2025
0abc0a8
tweak
Rich-Harris Apr 19, 2025
2797036
Merge branch 'main' into async
Rich-Harris Apr 19, 2025
ce09353
tidy up
Rich-Harris Apr 19, 2025
e49f81f
dont use flushSync
Rich-Harris Apr 19, 2025
e0e48b3
WIP
Rich-Harris Apr 19, 2025
8cc5961
tweak
Rich-Harris Apr 20, 2025
b48c12b
out of date
Rich-Harris Apr 20, 2025
e247f66
more
Rich-Harris Apr 20, 2025
d42b358
guarantee fork
Rich-Harris Apr 20, 2025
5000aae
forks.js -> batch.js
Rich-Harris Apr 20, 2025
d465537
rename forks to batches
Rich-Harris Apr 20, 2025
9b5f00b
fix
Rich-Harris Apr 20, 2025
5a3f7c2
simplify
Rich-Harris Apr 20, 2025
011741e
note to self
Rich-Harris Apr 20, 2025
623fb50
tweak
Rich-Harris Apr 20, 2025
4a56c2a
tweak
Rich-Harris Apr 20, 2025
f30fd26
privatise
Rich-Harris Apr 20, 2025
6eac199
failing test
Rich-Harris Apr 20, 2025
1f02fdf
note to self
Rich-Harris Apr 20, 2025
3ee25bb
reinstate scheduling optimisation
Rich-Harris Apr 20, 2025
e5579fd
WIP
Rich-Harris Apr 20, 2025
2e813f1
consistent behaviour
Rich-Harris Apr 20, 2025
6e26478
simplify
Rich-Harris Apr 20, 2025
5518e98
WIP
Rich-Harris Apr 20, 2025
c0ff1d0
tidy
Rich-Harris Apr 20, 2025
0f5b3cd
tweak
Rich-Harris Apr 21, 2025
32f753d
fix
Rich-Harris Apr 21, 2025
f73a5e9
compile playground with dev: false
Rich-Harris Apr 21, 2025
2087b3e
failing test
Rich-Harris Apr 21, 2025
ec81491
shuffle
Rich-Harris Apr 21, 2025
45f4cc5
WIP
Rich-Harris Apr 21, 2025
9e0bd4f
WIP
Rich-Harris Apr 21, 2025
0bc6e69
WIP
Rich-Harris Apr 21, 2025
43eeca9
WIP
Rich-Harris Apr 21, 2025
1cb4e24
WIP
Rich-Harris Apr 21, 2025
21e4c44
WIP
Rich-Harris Apr 21, 2025
1c31363
WIP
Rich-Harris Apr 21, 2025
4680f38
WIP
Rich-Harris Apr 21, 2025
29147fb
WIP
Rich-Harris Apr 21, 2025
abba96c
WIP
Rich-Harris Apr 21, 2025
6f8abda
fix
Rich-Harris Apr 21, 2025
48293d2
fix
Rich-Harris Apr 21, 2025
d7d528c
fix `$effect.pending()`
Rich-Harris Apr 23, 2025
b805245
fix
Rich-Harris Apr 25, 2025
734f56c
fix
Rich-Harris Apr 28, 2025
77e0e60
merge main
Rich-Harris Apr 28, 2025
d7f580d
fix changeset
Rich-Harris Apr 30, 2025
399bda5
lint
Rich-Harris May 3, 2025
a98b5ea
note to self
Rich-Harris May 3, 2025
fc18e26
failing test
Rich-Harris May 3, 2025
ed17212
fix
Rich-Harris May 3, 2025
78dd1e2
DRY
Rich-Harris May 3, 2025
13a9b70
failing test for linear order
Rich-Harris May 3, 2025
1dd383e
enforce linear order
Rich-Harris May 3, 2025
7762f29
update test
Rich-Harris May 4, 2025
8baf164
fix
Rich-Harris May 4, 2025
666a148
implement getAbortSignal
Rich-Harris May 4, 2025
357ff47
docs
Rich-Harris May 4, 2025
b61c6ad
fix
Rich-Harris May 4, 2025
b68dcdc
note to self
Rich-Harris May 4, 2025
5e8bcfa
tweak/fix
Rich-Harris May 4, 2025
3d0b6f7
update test
Rich-Harris May 4, 2025
48a781e
fix
Rich-Harris May 5, 2025
693262a
fix
Rich-Harris May 5, 2025
c599807
implement `settled`
Rich-Harris May 9, 2025
53b9b8f
merge main
Rich-Harris May 20, 2025
29406c9
merge main
Rich-Harris May 28, 2025
449ca14
merge main
Rich-Harris May 29, 2025
c72d091
oops
Rich-Harris May 29, 2025
95b9605
fix
Rich-Harris May 29, 2025
6625971
work around some quirk of the test environment
Rich-Harris May 30, 2025
15ea2df
Merge branch 'main' into async
Rich-Harris May 30, 2025
4136cf2
fix
Rich-Harris May 31, 2025
fcd51d4
tidy up
Rich-Harris May 31, 2025
f584d0d
fix
Rich-Harris May 31, 2025
7dc2019
lint
Rich-Harris May 31, 2025
a2bc5f7
add flag
Rich-Harris May 31, 2025
bc050c3
make everything non-breaking for people who dont opt in
Rich-Harris May 31, 2025
4d05ed1
disallow late setContext calls
Rich-Harris May 31, 2025
9b1e182
another test
Rich-Harris May 31, 2025
eaaee83
regenerate
Rich-Harris May 31, 2025
7e83cda
move
Rich-Harris May 31, 2025
2071160
keep order
Rich-Harris Jun 1, 2025
1a094e7
lint
Rich-Harris Jun 1, 2025
03273b7
chore: better HTML normalization test helper
Rich-Harris Jun 1, 2025
7c10b23
simplify
Rich-Harris Jun 1, 2025
73796c4
simplify/robustify
Rich-Harris Jun 1, 2025
3cf0b9e
Merge branch 'better-normalize-html' into async
Rich-Harris Jun 1, 2025
302dff2
don't write values to deriveds when time travelling
Rich-Harris Jun 2, 2025
b608ee2
add failing test
Rich-Harris Jun 3, 2025
1a42fc8
fix
Rich-Harris Jun 4, 2025
f72d1a6
merge
Rich-Harris Jun 4, 2025
8557077
we can remove this now
Rich-Harris Jun 4, 2025
c96d310
failing test
Rich-Harris Jun 4, 2025
17b2f22
fix
Rich-Harris Jun 4, 2025
d131e28
unused
Rich-Harris Jun 4, 2025
f8e4651
failing test
Rich-Harris Jun 4, 2025
fdb7a6d
fix
Rich-Harris Jun 4, 2025
23bd5c2
DRY
Rich-Harris Jun 4, 2025
00ba548
simplify
Rich-Harris Jun 4, 2025
cd24e51
tweak
Rich-Harris Jun 4, 2025
c8e47cc
tidy
Rich-Harris Jun 4, 2025
7f0072b
tidy
Rich-Harris Jun 4, 2025
dfa8d6b
yes it can, apparently
Rich-Harris Jun 4, 2025
aec7368
tidy up
Rich-Harris Jun 4, 2025
541ab97
unused
Rich-Harris Jun 4, 2025
b1960ce
complete merge
Rich-Harris Jun 5, 2025
37333df
WIP
Rich-Harris Jun 5, 2025
42d5c7e
simplify
Rich-Harris Jun 5, 2025
54e720b
merge
Rich-Harris Jun 5, 2025
0530c2c
debugging help
Rich-Harris Jun 5, 2025
c3ad8c4
WIP
Rich-Harris Jun 5, 2025
f3b7ce0
unused
Rich-Harris Jun 5, 2025
6cd7ef9
partial merge
Rich-Harris Jun 6, 2025
84e0790
merge main
Rich-Harris Jun 6, 2025
520d893
WIP
Rich-Harris Jun 6, 2025
c566d56
WIP
Rich-Harris Jun 6, 2025
60f8653
fix
Rich-Harris Jun 6, 2025
858dc35
add test
Rich-Harris Jun 6, 2025
68e2eee
rename
Rich-Harris Jun 6, 2025
5a05dc5
fix
Rich-Harris Jun 6, 2025
0bd5310
unused
Rich-Harris Jun 8, 2025
bc1a4ca
oops
Rich-Harris Jun 8, 2025
fe6c00d
merge main
Rich-Harris Jun 10, 2025
6e1c89f
merge main
Rich-Harris Jun 11, 2025
8733a5d
Merge branch 'main' into async
Rich-Harris Jun 12, 2025
1fb0b1f
merge main
Rich-Harris Jun 14, 2025
6efdc23
merge main
Rich-Harris Jun 15, 2025
61a11a5
chore: merge main into async branch (#16197)
dummdidumm Jun 18, 2025
3156a24
merge main
Rich-Harris Jun 24, 2025
5fda011
make batch.#deferred private
Rich-Harris Jun 24, 2025
ea0e269
fix settled when awaits occur inside pending boundary
Rich-Harris Jun 24, 2025
6b9f860
tweak
Rich-Harris Jun 24, 2025
66635c5
change behaviour of `tick()` to be requestAnimationFrame-based
Rich-Harris Jun 24, 2025
8d20a9a
get rid of a bunch of Promise.resolve chains
Rich-Harris Jun 24, 2025
2ae79f3
more
Rich-Harris Jun 24, 2025
0be1f6a
more
Rich-Harris Jun 24, 2025
680cb6a
Merge branch 'main' into async
Rich-Harris Jun 25, 2025
ac7b4a7
Merge branch 'main' into async
Rich-Harris Jun 25, 2025
e912f88
fix test
Rich-Harris Jun 25, 2025
0430c76
disallow `flushSync()` inside effects
Rich-Harris Jun 25, 2025
b400780
regenerate
Rich-Harris Jun 25, 2025
951d8e6
handle errors in block expressions
Rich-Harris Jun 25, 2025
fdd009b
make validate_each_keys async-aware
dummdidumm Jun 25, 2025
5c0a4a0
for unowned deriveds, throw errors lazily
Rich-Harris Jun 25, 2025
a0dba34
rename ASYNC_ERROR -> ERROR_VALUE, and avoid conflicts with other fla…
Rich-Harris Jun 25, 2025
da5b74a
invoke boundary directly
Rich-Harris Jun 25, 2025
c80d165
local effect pending
Rich-Harris Jun 25, 2025
ee21d9f
update test
Rich-Harris Jun 25, 2025
516ab3b
Merge branch 'async' into async-local-effect-pending
Rich-Harris Jun 25, 2025
8cd5635
fix
Rich-Harris Jun 25, 2025
8300327
fix
Rich-Harris Jun 25, 2025
c04a13b
fix weird bug in tests
Rich-Harris Jun 26, 2025
8a1ab92
Merge branch 'async-local-effect-pending' into async
Rich-Harris Jun 26, 2025
5c1ff99
delete old changeset that somehow got left over here
Rich-Harris Jun 26, 2025
042598c
Update .changeset/eleven-weeks-dance.md
Rich-Harris Jun 26, 2025
758c39d
update error details
Rich-Harris Jun 26, 2025
163009d
unused
Rich-Harris Jun 26, 2025
2961124
simplify
Rich-Harris Jun 26, 2025
f388568
tweak
Rich-Harris Jun 26, 2025
f34f28e
tweak
Rich-Harris Jun 26, 2025
1e2e57f
tweak
Rich-Harris Jun 26, 2025
a01249e
tweak
Rich-Harris Jun 26, 2025
2c2557a
tidy up
Rich-Harris Jun 26, 2025
2fd7628
handle errors in async block expressions
Rich-Harris Jun 26, 2025
5694c06
tweak
Rich-Harris Jun 26, 2025
591aeb0
groundwork for async attribute_effect
Rich-Harris Jun 26, 2025
7144f11
dry out
Rich-Harris Jun 26, 2025
d7a99b6
fix async directives
Rich-Harris Jun 26, 2025
650144a
tidy up
Rich-Harris Jun 26, 2025
334adc0
initialize option values before initing select values
Rich-Harris Jun 26, 2025
d89f318
simplify init_select
Rich-Harris Jun 26, 2025
58918e3
simplify
Rich-Harris Jun 26, 2025
38d458a
tweak
Rich-Harris Jun 26, 2025
39b7f41
tidy up
Rich-Harris Jun 26, 2025
5615fd3
tweak
Rich-Harris Jun 26, 2025
b459bb0
on second thoughts just simplify it here
Rich-Harris Jun 26, 2025
0c4af38
merge
Rich-Harris Jun 26, 2025
3070b9a
tidy
Rich-Harris Jun 26, 2025
336a526
merge main
Rich-Harris Jun 27, 2025
a018796
handle awaits in `<slot>`
Rich-Harris Jun 27, 2025
94d74f2
unused
Rich-Harris Jun 27, 2025
0c24129
tidy up
Rich-Harris Jun 27, 2025
12fffb9
tidy up
Rich-Harris Jun 27, 2025
2b44100
dry out
Rich-Harris Jun 27, 2025
2585516
dry out
Rich-Harris Jun 27, 2025
3eee9d5
Revert "dry out"
Rich-Harris Jun 27, 2025
7d6b603
dry out
Rich-Harris Jun 27, 2025
7bbb640
dry out
Rich-Harris Jun 27, 2025
a1a289e
use let for block-scoped stuff
Rich-Harris Jun 27, 2025
97f8110
dry out
Rich-Harris Jun 27, 2025
53137a1
dry out
Rich-Harris Jun 27, 2025
b3bcdaf
tidy up
Rich-Harris Jun 27, 2025
aa14305
only wrap awaits in `$.save` when necessary
Rich-Harris Jun 27, 2025
1c66278
oops
Rich-Harris Jun 27, 2025
88b9fdb
Merge branch 'main' into async
dummdidumm Jun 28, 2025
1a6cd7d
merge main
Rich-Harris Jun 30, 2025
aff0f50
merge main
Rich-Harris Jun 30, 2025
4d05b1b
remove TODO comment (just checked)
Rich-Harris Jun 30, 2025
73fb7a1
oops, leftover
Rich-Harris Jun 30, 2025
80de2ca
simplify
Rich-Harris Jun 30, 2025
ff0662a
unused
Rich-Harris Jun 30, 2025
e2bcd8c
remove logging
Rich-Harris Jun 30, 2025
2e854de
tweak
Rich-Harris Jun 30, 2025
f6976fd
unused
Rich-Harris Jun 30, 2025
9386b77
unused
Rich-Harris Jun 30, 2025
d96991a
remove logging
Rich-Harris Jul 1, 2025
36947c5
partial fix
Rich-Harris Jul 1, 2025
923d8f6
fix
Rich-Harris Jul 1, 2025
09792a0
merge main
Rich-Harris Jul 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eleven-weeks-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': minor
---

feat: support `await` in components when using the `experimental.async` compiler option
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ jobs:
- run: pnpm test
env:
CI: true
TestNoAsync:
permissions: {}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm playwright install chromium
- run: pnpm test runtime-runes
env:
CI: true
SVELTE_NO_ASYNC: true
Lint:
permissions: {}
runs-on: ubuntu-latest
Expand Down
18 changes: 18 additions & 0 deletions documentation/docs/98-reference/.generated/client-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ Effect cannot be created inside a `$derived` value that was not itself created i
Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops
```

### flush_sync_in_effect

```
Cannot use `flushSync` inside an effect
```

The `flushSync()` function can be used to flush any pending effects synchronously. It cannot be used if effects are currently being flushed — in other words, you can call it after a state change but _not_ inside an effect.

This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6.

### get_abort_signal_outside_reaction

```
Expand Down Expand Up @@ -116,6 +126,14 @@ Rest element properties of `$props()` such as `%property%` are readonly
The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
```

### set_context_after_init

```
`setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression
```

This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6.

### state_descriptors_fixed

```
Expand Down
16 changes: 16 additions & 0 deletions documentation/docs/98-reference/.generated/client-warnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ function add() {
}
```
### await_reactivity_loss
```
Detected reactivity loss
```
TODO

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO (and below)

### await_waterfall
```
An async value (%location%) was not read immediately after it resolved. This often indicates an unnecessary waterfall, which can slow down your app.
```
TODO
### binding_property_non_reactive
```
Expand Down
12 changes: 12 additions & 0 deletions documentation/docs/98-reference/.generated/compile-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,12 @@ Expected token %token%
Expected whitespace
```

### experimental_async

```
Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless the `experimental.async` compiler option is `true`
```

### export_undefined

```
Expand Down Expand Up @@ -534,6 +540,12 @@ The arguments keyword cannot be used within the template or at the top level of
%message%
```

### legacy_await_invalid

```
Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless in runes mode
```

### legacy_export_invalid

```
Expand Down
8 changes: 8 additions & 0 deletions documentation/docs/98-reference/.generated/shared-errors.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<!-- This file is generated by scripts/process-messages/index.js. Do not edit! -->

### await_outside_boundary

```
Cannot await outside a `<svelte:boundary>` with a `pending` snippet
```

TODO

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO


### invalid_default_snippet

```
Expand Down
14 changes: 14 additions & 0 deletions packages/svelte/messages/client-errors/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long

> Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops

## flush_sync_in_effect

> Cannot use `flushSync` inside an effect

The `flushSync()` function can be used to flush any pending effects synchronously. It cannot be used if effects are currently being flushed — in other words, you can call it after a state change but _not_ inside an effect.

This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6.

## get_abort_signal_outside_reaction

> `getAbortSignal()` can only be called inside an effect or derived
Expand Down Expand Up @@ -76,6 +84,12 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long

> The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files

## set_context_after_init

> `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression

This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6.

## state_descriptors_fixed

> Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.
Expand Down
12 changes: 12 additions & 0 deletions packages/svelte/messages/client-warnings/warnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ function add() {
}
```
## await_reactivity_loss
> Detected reactivity loss
TODO
## await_waterfall
> An async value (%location%) was not read immediately after it resolved. This often indicates an unnecessary waterfall, which can slow down your app.
TODO
## binding_property_non_reactive
> `%binding%` is binding to a non-reactive property
Expand Down
8 changes: 8 additions & 0 deletions packages/svelte/messages/compile-errors/script.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ This turned out to be buggy and unpredictable, particularly when working with de

> `$effect()` can only be used as an expression statement

## experimental_async

> Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless the `experimental.async` compiler option is `true`

## export_undefined

> `%name%` is not defined
Expand Down Expand Up @@ -98,6 +102,10 @@ This turned out to be buggy and unpredictable, particularly when working with de

> The arguments keyword cannot be used within the template or at the top level of a component

## legacy_await_invalid

> Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless in runes mode

## legacy_export_invalid

> Cannot use `export let` in runes mode — use `$props()` instead
Expand Down
6 changes: 6 additions & 0 deletions packages/svelte/messages/shared-errors/errors.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## await_outside_boundary

> Cannot await outside a `<svelte:boundary>` with a `pending` snippet

TODO

## invalid_default_snippet

> Cannot use `{@render children(...)}` if the parent component uses `let:` directives. Consider using a named snippet instead
Expand Down
3 changes: 3 additions & 0 deletions packages/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
"./internal/disclose-version": {
"default": "./src/internal/disclose-version.js"
},
"./internal/flags/async": {
"default": "./src/internal/flags/async.js"
},
"./internal/flags/legacy": {
"default": "./src/internal/flags/legacy.js"
},
Expand Down
18 changes: 18 additions & 0 deletions packages/svelte/src/compiler/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ export function effect_invalid_placement(node) {
e(node, 'effect_invalid_placement', `\`$effect()\` can only be used as an expression statement\nhttps://svelte.dev/e/effect_invalid_placement`);
}

/**
* Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless the `experimental.async` compiler option is `true`
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function experimental_async(node) {
e(node, 'experimental_async', `Cannot use \`await\` in deriveds and template expressions, or at the top level of a component, unless the \`experimental.async\` compiler option is \`true\`\nhttps://svelte.dev/e/experimental_async`);
}

/**
* `%name%` is not defined
* @param {null | number | NodeLike} node
Expand Down Expand Up @@ -235,6 +244,15 @@ export function invalid_arguments_usage(node) {
e(node, 'invalid_arguments_usage', `The arguments keyword cannot be used within the template or at the top level of a component\nhttps://svelte.dev/e/invalid_arguments_usage`);
}

/**
* Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless in runes mode
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function legacy_await_invalid(node) {
e(node, 'legacy_await_invalid', `Cannot use \`await\` in deriveds and template expressions, or at the top level of a component, unless in runes mode\nhttps://svelte.dev/e/legacy_await_invalid`);
}

/**
* Cannot use `export let` in runes mode — use `$props()` instead
* @param {null | number | NodeLike} node
Expand Down
5 changes: 4 additions & 1 deletion packages/svelte/src/compiler/migrate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ export function migrate(source, { filename, use_ts } = {}) {
...validate_component_options({}, ''),
...parsed_options,
customElementOptions,
filename: filename ?? '(unknown)'
filename: filename ?? '(unknown)',
experimental: {
async: true
}
};

const str = new MagicString(source);
Expand Down
2 changes: 2 additions & 0 deletions packages/svelte/src/compiler/phases/1-parse/state/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ export default function element(parser) {
} else {
element.tag = get_attribute_expression(definition);
}

element.metadata.expression = create_expression_metadata();
}

if (is_top_level_script_or_style) {
Expand Down
34 changes: 25 additions & 9 deletions packages/svelte/src/compiler/phases/2-analyze/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @import { Comment, Expression, Node, Program } from 'estree' */
/** @import { Binding, AST, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
/** @import { Expression, Node, Program } from 'estree' */
/** @import { Binding, AST, ValidatedCompileOptions, ValidatedModuleCompileOptions, ExpressionMetadata } from '#compiler' */
/** @import { AnalysisState, Visitors } from './types' */
/** @import { Analysis, ComponentAnalysis, Js, ReactiveStatement, Template } from '../types' */
import { walk } from 'zimmerframe';
Expand All @@ -22,6 +22,7 @@ import { AssignmentExpression } from './visitors/AssignmentExpression.js';
import { AttachTag } from './visitors/AttachTag.js';
import { Attribute } from './visitors/Attribute.js';
import { AwaitBlock } from './visitors/AwaitBlock.js';
import { AwaitExpression } from './visitors/AwaitExpression.js';
import { BindDirective } from './visitors/BindDirective.js';
import { CallExpression } from './visitors/CallExpression.js';
import { ClassBody } from './visitors/ClassBody.js';
Expand Down Expand Up @@ -140,6 +141,7 @@ const visitors = {
AttachTag,
Attribute,
AwaitBlock,
AwaitExpression,
BindDirective,
CallExpression,
ClassBody,
Expand Down Expand Up @@ -211,9 +213,14 @@ function js(script, root, allow_reactive_declarations, parent) {
body: []
};

const { scope, scopes } = create_scopes(ast, root, allow_reactive_declarations, parent);
const { scope, scopes, has_await } = create_scopes(
ast,
root,
allow_reactive_declarations,
parent
);

return { ast, scope, scopes };
return { ast, scope, scopes, has_await };
}

/**
Expand Down Expand Up @@ -244,7 +251,7 @@ export function analyze_module(source, options) {
state.set_source(source);
const ast = parse(source, comments, false, false);

const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, null);
const { scope, scopes, has_await } = create_scopes(ast, new ScopeRoot(), false, null);

for (const [name, references] of scope.references) {
if (name[0] !== '$' || RESERVED.includes(name)) continue;
Expand All @@ -261,12 +268,13 @@ export function analyze_module(source, options) {

/** @type {Analysis} */
const analysis = {
module: { ast, scope, scopes },
module: { ast, scope, scopes, has_await },
name: options.filename,
accessors: false,
runes: true,
immutable: true,
tracing: false,
async_deriveds: new Set(),
comments,
classes: new Map()
};
Expand Down Expand Up @@ -314,7 +322,12 @@ export function analyze_component(root, source, options) {
const module = js(root.module, scope_root, false, null);
const instance = js(root.instance, scope_root, true, module.scope);

const { scope, scopes } = create_scopes(root.fragment, scope_root, false, instance.scope);
const { scope, scopes, has_await } = create_scopes(
root.fragment,
scope_root,
false,
instance.scope
);

/** @type {Template} */
const template = { ast: root.fragment, scope, scopes };
Expand Down Expand Up @@ -422,7 +435,9 @@ export function analyze_component(root, source, options) {

const component_name = get_component_name(options.filename);

const runes = options.runes ?? Array.from(module.scope.references.keys()).some(is_rune);
const runes =
options.runes ??
(has_await || instance.has_await || Array.from(module.scope.references.keys()).some(is_rune));

if (!runes) {
for (let check of synthetic_stores_legacy_check) {
Expand Down Expand Up @@ -512,7 +527,8 @@ export function analyze_component(root, source, options) {
source,
undefined_exports: new Map(),
snippet_renderers: new Map(),
snippets: new Set()
snippets: new Set(),
async_deriveds: new Set()
};

state.reset({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** @import { AwaitExpression } from 'estree' */
/** @import { Context } from '../types' */
import * as e from '../../../errors.js';

/**
* @param {AwaitExpression} node
* @param {Context} context
*/
export function AwaitExpression(node, context) {
let suspend = context.state.ast_type === 'instance' && context.state.function_depth === 1;

if (context.state.expression) {
context.state.expression.has_await = true;
suspend = true;
}

// disallow top-level `await` or `await` in template expressions
// unless a) in runes mode and b) opted into `experimental.async`
if (suspend) {
if (!context.state.options.experimental.async) {
e.experimental_async(node);
}

if (!context.state.analysis.runes) {
e.legacy_await_invalid(node);
}
}

context.next();
}
Loading
Loading