Skip to content

Commit 4d05ed1

Browse files
committed
disallow late setContext calls
1 parent bc050c3 commit 4d05ed1

File tree

6 files changed

+64
-1
lines changed

6 files changed

+64
-1
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ Rest element properties of `$props()` such as `%property%` are readonly
110110
The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
111111
```
112112

113+
### set_context_after_init
114+
115+
```
116+
`setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression
117+
```
118+
113119
### state_descriptors_fixed
114120

115121
```

packages/svelte/messages/client-errors/errors.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
7272

7373
> The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
7474
75+
## set_context_after_init
76+
77+
> `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression
78+
7579
## state_descriptors_fixed
7680

7781
> Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.

packages/svelte/src/internal/client/context.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { DEV } from 'esm-env';
44
import { lifecycle_outside_component } from '../shared/errors.js';
5+
import * as e from './errors.js';
56
import { source } from './reactivity/sources.js';
67
import {
78
active_effect,
@@ -10,7 +11,7 @@ import {
1011
set_active_reaction
1112
} from './runtime.js';
1213
import { effect, teardown } from './reactivity/effects.js';
13-
import { legacy_mode_flag } from '../flags/index.js';
14+
import { async_mode_flag, legacy_mode_flag } from '../flags/index.js';
1415

1516
/** @type {ComponentContext | null} */
1617
export let component_context = null;
@@ -65,6 +66,13 @@ export function getContext(key) {
6566
*/
6667
export function setContext(key, context) {
6768
const context_map = get_or_init_context_map('setContext');
69+
70+
if (async_mode_flag) {
71+
if (/** @type {ComponentContext} */ (component_context).m) {
72+
e.set_context_after_init();
73+
}
74+
}
75+
6876
context_map.set(key, context);
6977
return context;
7078
}

packages/svelte/src/internal/client/errors.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,21 @@ export function hydration_failed() {
197197
}
198198
}
199199

200+
/**
201+
* `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression
202+
* @returns {never}
203+
*/
204+
export function set_context_after_init() {
205+
if (DEV) {
206+
const error = new Error(`set_context_after_init\n\`setContext\` must be called when a component first initializes, not in a subsequent effect or after an \`await\` expression\nhttps://svelte.dev/e/set_context_after_init`);
207+
208+
error.name = 'Svelte error';
209+
throw error;
210+
} else {
211+
throw new Error(`https://svelte.dev/e/set_context_after_init`);
212+
}
213+
}
214+
200215
/**
201216
* Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}`
202217
* @returns {never}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ target, assert, logs }) {
6+
const button = target.querySelector('button');
7+
8+
flushSync(() => button?.click());
9+
assert.ok(logs[0].startsWith('set_context_after_init'));
10+
}
11+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
import { setContext } from 'svelte';
3+
4+
let condition = $state(false);
5+
6+
$effect(() => {
7+
if (condition) {
8+
try {
9+
setContext('potato', {});
10+
} catch (e) {
11+
console.log(e.message);
12+
}
13+
}
14+
});
15+
</script>
16+
17+
<button onclick={() => condition = !condition}>
18+
{condition}
19+
</button>

0 commit comments

Comments
 (0)