Skip to content

Commit 148ffd2

Browse files
committed
warn on reactivity loss
1 parent b4ee140 commit 148ffd2

File tree

7 files changed

+79
-14
lines changed

7 files changed

+79
-14
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ function add() {
3434
}
3535
```
3636
37+
### await_reactivity_loss
38+
39+
```
40+
Detected reactivity loss
41+
```
42+
43+
TODO
44+
3745
### await_waterfall
3846
3947
```

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ function add() {
3030
}
3131
```
3232
33+
## await_reactivity_loss
34+
35+
> Detected reactivity loss
36+
37+
TODO
38+
3339
## await_waterfall
3440
3541
> Detected an unnecessary async waterfall
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
/** @import { AwaitExpression, Expression } from 'estree' */
22
/** @import { Context } from '../types' */
3+
import { dev } from '../../../../state.js';
34
import * as b from '../../../../utils/builders.js';
45

56
/**
67
* @param {AwaitExpression} node
78
* @param {Context} context
89
*/
910
export function AwaitExpression(node, context) {
10-
const suspend = context.state.analysis.context_preserving_awaits.has(node);
11+
const save = context.state.analysis.context_preserving_awaits.has(node);
1112

12-
if (!suspend) {
13-
return context.next();
13+
if (dev || save) {
14+
return b.call(
15+
b.await(
16+
b.call(
17+
'$.save',
18+
node.argument && /** @type {Expression} */ (context.visit(node.argument)),
19+
!save && b.false
20+
)
21+
)
22+
);
1423
}
1524

16-
return b.call(
17-
b.await(
18-
b.call('$.save', node.argument && /** @type {Expression} */ (context.visit(node.argument)))
19-
)
20-
);
25+
return context.next();
2126
}

packages/svelte/src/internal/client/dom/blocks/boundary.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
import { get_next_sibling } from '../operations.js';
3131
import { queue_boundary_micro_task } from '../task.js';
3232
import * as e from '../../../shared/errors.js';
33+
import { DEV } from 'esm-env';
34+
import { from_async_derived, set_from_async_derived } from '../../reactivity/deriveds.js';
3335

3436
const ASYNC_INCREMENT = Symbol();
3537
const ASYNC_DECREMENT = Symbol();
@@ -340,15 +342,23 @@ function move_effect(effect, fragment) {
340342
}
341343
}
342344

343-
export function capture() {
345+
export function capture(track = true) {
344346
var previous_effect = active_effect;
345347
var previous_reaction = active_reaction;
346348
var previous_component_context = component_context;
347349

350+
if (DEV && !track) {
351+
var was_from_async_derived = from_async_derived;
352+
}
353+
348354
return function restore() {
349-
set_active_effect(previous_effect);
350-
set_active_reaction(previous_reaction);
351-
set_component_context(previous_component_context);
355+
if (track) {
356+
set_active_effect(previous_effect);
357+
set_active_reaction(previous_reaction);
358+
set_component_context(previous_component_context);
359+
} else if (DEV) {
360+
set_from_async_derived(was_from_async_derived);
361+
}
352362

353363
// prevent the active effect from outstaying its welcome
354364
queue_boundary_micro_task(exit);
@@ -390,10 +400,11 @@ export function suspend() {
390400
/**
391401
* @template T
392402
* @param {Promise<T>} promise
403+
* @param {boolean} [track]
393404
* @returns {Promise<() => T>}
394405
*/
395-
export async function save(promise) {
396-
var restore = capture();
406+
export async function save(promise, track = true) {
407+
var restore = capture(track);
397408
var value = await promise;
398409

399410
return () => {

packages/svelte/src/internal/client/reactivity/deriveds.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ import { tracing_mode_flag } from '../../flags/index.js';
2929
import { capture, suspend } from '../dom/blocks/boundary.js';
3030
import { component_context } from '../context.js';
3131

32+
/** @type {Effect | null} */
33+
export let from_async_derived = null;
34+
35+
/** @param {Effect | null} v */
36+
export function set_from_async_derived(v) {
37+
from_async_derived = v;
38+
}
39+
3240
/**
3341
* @template V
3442
* @param {() => V} fn
@@ -88,8 +96,11 @@ export function async_derived(fn) {
8896
var promise = /** @type {Promise<V>} */ (/** @type {unknown} */ (undefined));
8997
var value = source(/** @type {V} */ (undefined));
9098

99+
// TODO this isn't a block
91100
block(async () => {
101+
if (DEV) from_async_derived = active_effect;
92102
var current = (promise = fn());
103+
if (DEV) from_async_derived = null;
93104

94105
var restore = capture();
95106
var unsuspend = suspend();
@@ -103,6 +114,8 @@ export function async_derived(fn) {
103114

104115
if (promise === current) {
105116
restore();
117+
from_async_derived = null;
118+
106119
internal_set(value, v);
107120
}
108121
} catch (e) {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
destroy_derived,
3838
destroy_derived_effects,
3939
execute_derived,
40+
from_async_derived,
4041
update_derived
4142
} from './reactivity/deriveds.js';
4243
import * as e from './errors.js';
@@ -51,6 +52,7 @@ import {
5152
set_dev_current_component_function
5253
} from './context.js';
5354
import { add_boundary_effect, commit_boundary } from './dom/blocks/boundary.js';
55+
import * as w from './warnings.js';
5456

5557
const FLUSH_MICROTASK = 0;
5658
const FLUSH_SYNC = 1;
@@ -967,6 +969,15 @@ export function get(signal) {
967969
captured_signals.add(signal);
968970
}
969971

972+
if (DEV && from_async_derived) {
973+
var tracking = (from_async_derived.f & REACTION_IS_UPDATING) !== 0;
974+
var was_read = from_async_derived.deps !== null && from_async_derived.deps.includes(signal);
975+
976+
if (!tracking && !was_read) {
977+
w.await_reactivity_loss();
978+
}
979+
}
980+
970981
// Register the dependency on the current reaction signal.
971982
if (active_reaction !== null && !untracking) {
972983
if (derived_sources !== null && derived_sources.includes(signal)) {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ export function assignment_value_stale(property, location) {
1818
}
1919
}
2020

21+
/**
22+
* Detected reactivity loss
23+
*/
24+
export function await_reactivity_loss() {
25+
if (DEV) {
26+
console.warn(`%c[svelte] await_reactivity_loss\n%cDetected reactivity loss\nhttps://svelte.dev/e/await_reactivity_loss`, bold, normal);
27+
} else {
28+
console.warn(`https://svelte.dev/e/await_reactivity_loss`);
29+
}
30+
}
31+
2132
/**
2233
* Detected an unnecessary async waterfall
2334
*/

0 commit comments

Comments
 (0)