Skip to content

Commit aa29a85

Browse files
authored
chore: move reactivity code around (#10696)
* move some code * split computations.js into deriveds.js and effects.js * move reactivity types into separate .d.ts file * move some signal code --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 2d15c9d commit aa29a85

File tree

19 files changed

+332
-316
lines changed

19 files changed

+332
-316
lines changed

packages/svelte/src/internal/client/custom-element.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createClassComponent } from '../../legacy/legacy-client.js';
22
import { destroy_signal } from './runtime.js';
3-
import { render_effect } from './reactivity/computations.js';
3+
import { render_effect } from './reactivity/effects.js';
44
import { open, close } from './render.js';
55
import { define_property } from './utils.js';
66

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
flushSync,
99
push_destroy_fn
1010
} from '../../runtime.js';
11-
import { render_effect } from '../../reactivity/computations.js';
11+
import { render_effect } from '../../reactivity/effects.js';
1212
import { trigger_transitions } from '../../transitions.js';
1313
import { AWAIT_BLOCK, UNINITIALIZED } from '../../constants.js';
1414

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,9 @@ import {
1616
} from '../../hydration.js';
1717
import { clear_text_content, empty, map_get, map_set } from '../../operations.js';
1818
import { insert, remove } from '../../reconciler.js';
19-
import {
20-
current_block,
21-
destroy_signal,
22-
execute_effect,
23-
push_destroy_fn,
24-
set_signal_value
25-
} from '../../runtime.js';
26-
import { render_effect } from '../../reactivity/computations.js';
27-
import { source, mutable_source } from '../../reactivity/sources.js';
19+
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
20+
import { render_effect } from '../../reactivity/effects.js';
21+
import { source, mutable_source, set_signal_value } from '../../reactivity/sources.js';
2822
import { trigger_transitions } from '../../transitions.js';
2923
import { is_array } from '../../utils.js';
3024
import { EACH_BLOCK, EACH_ITEM_BLOCK } from '../../constants.js';

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from '../../hydration.js';
88
import { remove } from '../../reconciler.js';
99
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
10-
import { render_effect } from '../../reactivity/computations.js';
10+
import { render_effect } from '../../reactivity/effects.js';
1111
import { trigger_transitions } from '../../transitions.js';
1212

1313
/** @returns {import('../../types.js').IfBlock} */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { UNINITIALIZED, KEY_BLOCK } from '../../constants.js';
22
import { hydrate_block_anchor } from '../../hydration.js';
33
import { remove } from '../../reconciler.js';
44
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
5-
import { render_effect } from '../../reactivity/computations.js';
5+
import { render_effect } from '../../reactivity/effects.js';
66
import { trigger_transitions } from '../../transitions.js';
77
import { safe_not_equal } from '../../reactivity/equality.js';
88

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { DEV } from 'esm-env';
22
import {
33
get,
4-
set,
54
updating_derived,
65
batch_inspect,
76
current_component_context,
8-
untrack,
9-
set_signal_value
7+
untrack
108
} from './runtime.js';
11-
import { effect_active } from './reactivity/computations.js';
9+
import { effect_active } from './reactivity/effects.js';
1210
import {
1311
array_prototype,
1412
define_property,
@@ -20,7 +18,7 @@ import {
2018
object_prototype
2119
} from './utils.js';
2220
import { add_owner, check_ownership, strip_owner } from './dev/ownership.js';
23-
import { mutable_source, source } from './reactivity/sources.js';
21+
import { mutable_source, source, set, set_signal_value } from './reactivity/sources.js';
2422
import { STATE_SYMBOL, UNINITIALIZED } from './constants.js';
2523

2624
/**
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { CLEAN, DERIVED, UNINITIALIZED, UNOWNED } from '../constants.js';
2+
import { current_block, current_consumer, current_effect } from '../runtime.js';
3+
import { create_computation_signal, push_reference } from './effects.js';
4+
import { default_equals, safe_equal } from './equality.js';
5+
6+
/**
7+
* @template V
8+
* @param {() => V} fn
9+
* @returns {import('../types.js').ComputationSignal<V>}
10+
*/
11+
/*#__NO_SIDE_EFFECTS__*/
12+
export function derived(fn) {
13+
const is_unowned = current_effect === null;
14+
const flags = is_unowned ? DERIVED | UNOWNED : DERIVED;
15+
const signal = /** @type {import('../types.js').ComputationSignal<V>} */ (
16+
create_computation_signal(flags | CLEAN, UNINITIALIZED, current_block)
17+
);
18+
signal.i = fn;
19+
signal.e = default_equals;
20+
if (current_consumer !== null) {
21+
push_reference(current_consumer, signal);
22+
}
23+
return signal;
24+
}
25+
26+
/**
27+
* @template V
28+
* @param {() => V} fn
29+
* @returns {import('../types.js').ComputationSignal<V>}
30+
*/
31+
/*#__NO_SIDE_EFFECTS__*/
32+
export function derived_safe_equal(fn) {
33+
const signal = derived(fn);
34+
signal.e = safe_equal;
35+
return signal;
36+
}

packages/svelte/src/internal/client/reactivity/computations.js renamed to packages/svelte/src/internal/client/reactivity/effects.js

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
* @param {V} value
2828
* @param {import('../types.js').Block | null} block
2929
*/
30-
function create_computation_signal(flags, value, block) {
30+
export function create_computation_signal(flags, value, block) {
3131
/** @type {import('../types.js').ComputationSignal<V>} */
3232
const signal = {
3333
b: block,
@@ -224,35 +224,3 @@ export function render_effect(fn, block = current_block, managed = false, sync =
224224
}
225225
return internal_create_effect(flags, /** @type {any} */ (fn), sync, block, true);
226226
}
227-
228-
/**
229-
* @template V
230-
* @param {() => V} fn
231-
* @returns {import('../types.js').ComputationSignal<V>}
232-
*/
233-
/*#__NO_SIDE_EFFECTS__*/
234-
export function derived(fn) {
235-
const is_unowned = current_effect === null;
236-
const flags = is_unowned ? DERIVED | UNOWNED : DERIVED;
237-
const signal = /** @type {import('../types.js').ComputationSignal<V>} */ (
238-
create_computation_signal(flags | CLEAN, UNINITIALIZED, current_block)
239-
);
240-
signal.i = fn;
241-
signal.e = default_equals;
242-
if (current_consumer !== null) {
243-
push_reference(current_consumer, signal);
244-
}
245-
return signal;
246-
}
247-
248-
/**
249-
* @template V
250-
* @param {() => V} fn
251-
* @returns {import('../types.js').ComputationSignal<V>}
252-
*/
253-
/*#__NO_SIDE_EFFECTS__*/
254-
export function derived_safe_equal(fn) {
255-
const signal = derived(fn);
256-
signal.e = safe_equal;
257-
return signal;
258-
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @param {((value?: number) => number)} fn
3+
* @param {1 | -1} [d]
4+
* @returns {number}
5+
*/
6+
export function update_prop(fn, d = 1) {
7+
const value = fn();
8+
fn(value + d);
9+
return value;
10+
}
11+
12+
/**
13+
* @param {((value?: number) => number)} fn
14+
* @param {1 | -1} [d]
15+
* @returns {number}
16+
*/
17+
export function update_pre_prop(fn, d = 1) {
18+
const value = fn() + d;
19+
fn(value);
20+
return value;
21+
}

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

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
import { DEV } from 'esm-env';
2-
import { current_component_context } from '../runtime.js';
2+
import {
3+
current_component_context,
4+
current_consumer,
5+
current_dependencies,
6+
current_effect,
7+
current_untracked_writes,
8+
current_untracking,
9+
flushSync,
10+
get,
11+
ignore_mutation_validation,
12+
is_batching_effect,
13+
is_runes,
14+
mark_signal_consumers,
15+
schedule_effect,
16+
set_current_untracked_writes,
17+
set_last_inspected_signal,
18+
set_signal_status,
19+
untrack
20+
} from '../runtime.js';
321
import { default_equals, safe_equal } from './equality.js';
4-
import { CLEAN, SOURCE } from '../constants.js';
22+
import { CLEAN, DERIVED, DIRTY, MANAGED, SOURCE } from '../constants.js';
523

624
/**
725
* @template V
@@ -68,3 +86,109 @@ function create_source_signal(flags, value) {
6886
w: 0
6987
};
7088
}
89+
90+
/**
91+
* @template V
92+
* @param {import('./types.js').Signal<V>} signal
93+
* @param {V} value
94+
* @returns {V}
95+
*/
96+
export function set(signal, value) {
97+
set_signal_value(signal, value);
98+
return value;
99+
}
100+
101+
/**
102+
* @template V
103+
* @param {import('./types.js').Signal<V>} signal
104+
* @param {V} value
105+
* @returns {void}
106+
*/
107+
export function set_sync(signal, value) {
108+
flushSync(() => set(signal, value));
109+
}
110+
111+
/**
112+
* @template V
113+
* @param {import('./types.js').Signal<V>} source
114+
* @param {V} value
115+
*/
116+
export function mutate(source, value) {
117+
set_signal_value(
118+
source,
119+
untrack(() => get(source))
120+
);
121+
return value;
122+
}
123+
124+
/**
125+
* @template V
126+
* @param {import('./types.js').Signal<V>} signal
127+
* @param {V} value
128+
* @returns {void}
129+
*/
130+
export function set_signal_value(signal, value) {
131+
if (
132+
!current_untracking &&
133+
!ignore_mutation_validation &&
134+
current_consumer !== null &&
135+
is_runes(null) &&
136+
(current_consumer.f & DERIVED) !== 0
137+
) {
138+
throw new Error(
139+
'ERR_SVELTE_UNSAFE_MUTATION' +
140+
(DEV
141+
? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " +
142+
'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' +
143+
'to be reactive do not use the "$state" rune for that declaration.'
144+
: '')
145+
);
146+
}
147+
if (
148+
(signal.f & SOURCE) !== 0 &&
149+
!(/** @type {import('#client').EqualsFunctions} */ (signal.e)(value, signal.v))
150+
) {
151+
signal.v = value;
152+
// Increment write version so that unowned signals can properly track dirtyness
153+
signal.w++;
154+
// If the current signal is running for the first time, it won't have any
155+
// consumers as we only allocate and assign the consumers after the signal
156+
// has fully executed. So in the case of ensuring it registers the consumer
157+
// properly for itself, we need to ensure the current effect actually gets
158+
// scheduled. i.e:
159+
//
160+
// $effect(() => x++)
161+
//
162+
// We additionally want to skip this logic for when ignore_mutation_validation is
163+
// true, as stores write to source signal on initialization.
164+
if (
165+
is_runes(null) &&
166+
!ignore_mutation_validation &&
167+
current_effect !== null &&
168+
current_effect.c === null &&
169+
(current_effect.f & CLEAN) !== 0 &&
170+
(current_effect.f & MANAGED) === 0
171+
) {
172+
if (current_dependencies !== null && current_dependencies.includes(signal)) {
173+
set_signal_status(current_effect, DIRTY);
174+
schedule_effect(current_effect, false);
175+
} else {
176+
if (current_untracked_writes === null) {
177+
set_current_untracked_writes([signal]);
178+
} else {
179+
current_untracked_writes.push(signal);
180+
}
181+
}
182+
}
183+
mark_signal_consumers(signal, DIRTY, true);
184+
185+
// @ts-expect-error
186+
if (DEV && signal.inspect) {
187+
if (is_batching_effect) {
188+
set_last_inspected_signal(/** @type {import('./types.js').SignalDebug} */ (signal));
189+
} else {
190+
for (const fn of /** @type {import('./types.js').SignalDebug} */ (signal).inspect) fn();
191+
}
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)