Skip to content

Commit 9b1e0d4

Browse files
committed
WIP
1 parent 22ae61b commit 9b1e0d4

File tree

15 files changed

+80
-19
lines changed

15 files changed

+80
-19
lines changed

packages/svelte/src/ambient.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ declare namespace $derived {
196196
*/
197197
export function by<T>(fn: () => T): T;
198198

199+
export function diff<T>(fn: (patch: (fn: () => void) => void) => T): T;
200+
199201
// prevent intellisense from being unhelpful
200202
/** @deprecated */
201203
export const apply: never;

packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,18 @@ export function CallExpression(node, context) {
7777
case '$state.raw':
7878
case '$derived':
7979
case '$derived.by':
80+
case '$derived.diff':
8081
if (
8182
parent.type !== 'VariableDeclarator' &&
8283
!(parent.type === 'PropertyDefinition' && !parent.static && !parent.computed)
8384
) {
8485
e.state_invalid_placement(node, rune);
8586
}
8687

87-
if ((rune === '$derived' || rune === '$derived.by') && node.arguments.length !== 1) {
88+
if (
89+
(rune === '$derived' || rune === '$derived.by' || rune === '$derived.diff') &&
90+
node.arguments.length !== 1
91+
) {
8892
e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
8993
} else if (rune === '$state' && node.arguments.length > 1) {
9094
e.rune_invalid_arguments_length(node, rune, 'zero or one arguments');

packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function ClassBody(node, context) {
1717
definition.value?.type === 'CallExpression'
1818
) {
1919
const rune = get_rune(definition.value, context.state.scope);
20-
if (rune === '$derived' || rune === '$derived.by') {
20+
if (rune === '$derived' || rune === '$derived.by' || rune === '$derived.diff') {
2121
private_derived_state.push(definition.key.name);
2222
}
2323
}

packages/svelte/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function VariableDeclarator(node, context) {
2929
rune === '$state.raw' ||
3030
rune === '$derived' ||
3131
rune === '$derived.by' ||
32+
rune === '$derived.diff' ||
3233
rune === '$props'
3334
) {
3435
for (const path of paths) {
@@ -39,7 +40,7 @@ export function VariableDeclarator(node, context) {
3940
? 'state'
4041
: rune === '$state.raw'
4142
? 'raw_state'
42-
: rune === '$derived' || rune === '$derived.by'
43+
: rune === '$derived' || rune === '$derived.by' || rune === '$derived.diff'
4344
? 'derived'
4445
: path.is_rest
4546
? 'rest_prop'

packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export function ClassBody(node, context) {
4646
rune === '$state' ||
4747
rune === '$state.raw' ||
4848
rune === '$derived' ||
49-
rune === '$derived.by'
49+
rune === '$derived.by' ||
50+
rune === '$derived.diff'
5051
) {
5152
/** @type {StateField} */
5253
const field = {

packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,15 @@ export function VariableDeclaration(node, context) {
156156
continue;
157157
}
158158

159-
if (rune === '$derived' || rune === '$derived.by') {
159+
if (rune === '$derived' || rune === '$derived.by' || rune === '$derived.diff') {
160160
if (declarator.id.type === 'Identifier') {
161161
declarations.push(
162162
b.declarator(
163163
declarator.id,
164-
b.call('$.derived', rune === '$derived.by' ? value : b.thunk(value))
164+
b.call(
165+
rune === '$derived.diff' ? '$.derived_diff' : '$.derived',
166+
rune === '$derived.by' || rune === '$derived.diff' ? value : b.thunk(value)
167+
)
165168
)
166169
);
167170
} else {
@@ -180,7 +183,13 @@ export function VariableDeclaration(node, context) {
180183
rhs = b.call('$.get', id);
181184

182185
declarations.push(
183-
b.declarator(id, b.call('$.derived', rune === '$derived.by' ? value : b.thunk(value)))
186+
b.declarator(
187+
id,
188+
b.call(
189+
rune === '$derived.diff' ? '$.derived_diff' : '$.derived',
190+
rune === '$derived.by' || rune === '$derived.diff' ? value : b.thunk(value)
191+
)
192+
)
184193
);
185194
}
186195

packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function ClassBody(node, context) {
3636

3737
if (definition.value?.type === 'CallExpression') {
3838
const rune = get_rune(definition.value, context.state.scope);
39-
if (rune === '$derived' || rune === '$derived.by') {
39+
if (rune === '$derived' || rune === '$derived.by' || rune === '$derived.diff') {
4040
/** @type {StateField} */
4141
const field = {
4242
kind: rune === '$derived.by' ? 'derived_by' : 'derived',

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ export const EFFECT_RAN = 1 << 14;
1616
export const EFFECT_TRANSPARENT = 1 << 15;
1717
/** Svelte 4 legacy mode props need to be handled with deriveds and be recognized elsewhere, hence the dedicated flag */
1818
export const LEGACY_DERIVED_PROP = 1 << 16;
19-
export const INSPECT_EFFECT = 1 << 17;
20-
export const HEAD_EFFECT = 1 << 18;
21-
export const EFFECT_HAS_DERIVED = 1 << 19;
19+
export const DERIVED_DIFF = 1 << 17;
20+
export const INSPECT_EFFECT = 1 << 18;
21+
export const HEAD_EFFECT = 1 << 19;
22+
export const EFFECT_HAS_DERIVED = 1 << 20;
2223

2324
export const STATE_SYMBOL = Symbol('$state');
2425
export const STATE_SYMBOL_METADATA = Symbol('$state metadata');

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export {
9292
template_with_script,
9393
text
9494
} from './dom/template.js';
95-
export { derived, derived_safe_equal } from './reactivity/deriveds.js';
95+
export { derived, derived_diff, derived_safe_equal } from './reactivity/deriveds.js';
9696
export {
9797
effect_tracking,
9898
effect_root,

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
DIRTY,
88
EFFECT_HAS_DERIVED,
99
MAYBE_DIRTY,
10+
DERIVED_DIFF,
1011
UNOWNED
1112
} from '../constants.js';
1213
import {
@@ -18,7 +19,8 @@ import {
1819
update_reaction,
1920
increment_version,
2021
set_active_effect,
21-
component_context
22+
component_context,
23+
get
2224
} from '../runtime.js';
2325
import { equals, safe_equals } from './equality.js';
2426
import * as e from '../errors.js';
@@ -31,8 +33,8 @@ import { inspect_effects, set_inspect_effects } from './sources.js';
3133
* @returns {Derived<V>}
3234
*/
3335
/*#__NO_SIDE_EFFECTS__*/
34-
export function derived(fn) {
35-
var flags = DERIVED | DIRTY;
36+
export function derived(fn, _flags = 0) {
37+
var flags = DERIVED | DIRTY | _flags;
3638

3739
if (active_effect === null) {
3840
flags |= UNOWNED;
@@ -68,6 +70,20 @@ export function derived(fn) {
6870
return signal;
6971
}
7072

73+
/**
74+
* @template T
75+
* @param {(diff: () => void) => T} fn
76+
*/
77+
export function derived_diff(fn) {
78+
return derived(() => {
79+
const patch = (/** @type {() => void} */ fn) => {
80+
const p = derived(fn);
81+
get(p);
82+
}
83+
return fn(patch);
84+
}, DERIVED_DIFF);
85+
}
86+
7187
/**
7288
* @template V
7389
* @param {() => V} fn

0 commit comments

Comments
 (0)