Skip to content

Commit 765d01d

Browse files
authored
correctly inspect derived values (#9731)
Co-authored-by: Rich Harris <[email protected]>
1 parent 1108587 commit 765d01d

File tree

6 files changed

+109
-5
lines changed

6 files changed

+109
-5
lines changed

.changeset/lemon-geese-drum.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: improve `$inspect` type definition

.changeset/two-dragons-yell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: correctly inspect derived values

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

+43-2
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,36 @@ function create_source_signal(flags, value) {
191191
* @param {import('./types.js').SignalFlags} flags
192192
* @param {V} value
193193
* @param {import('./types.js').Block | null} block
194-
* @returns {import('./types.js').ComputationSignal<V>}
194+
* @returns {import('./types.js').ComputationSignal<V> | import('./types.js').ComputationSignal<V> & import('./types.js').SourceSignalDebug}
195195
*/
196196
function create_computation_signal(flags, value, block) {
197+
if (DEV) {
198+
return {
199+
// block
200+
b: block,
201+
// consumers
202+
c: null,
203+
// destroy
204+
d: null,
205+
// equals
206+
e: null,
207+
// flags
208+
f: flags,
209+
// init
210+
i: null,
211+
// references
212+
r: null,
213+
// value
214+
v: value,
215+
// context: We can remove this if we get rid of beforeUpdate/afterUpdate
216+
x: null,
217+
// destroy
218+
y: null,
219+
// this is for DEV only
220+
inspect: new Set()
221+
};
222+
}
223+
197224
return {
198225
// block
199226
b: block,
@@ -671,6 +698,12 @@ function update_derived(signal, force_schedule) {
671698
if (!equals(value, signal.v)) {
672699
signal.v = value;
673700
mark_signal_consumers(signal, DIRTY, force_schedule);
701+
702+
// @ts-expect-error
703+
if (DEV && signal.inspect && force_schedule) {
704+
// @ts-expect-error
705+
for (const fn of signal.inspect) fn();
706+
}
674707
}
675708
}
676709

@@ -836,7 +869,15 @@ export function get(signal) {
836869
}
837870

838871
if ((flags & DERIVED) !== 0 && is_signal_dirty(signal)) {
839-
update_derived(/** @type {import('./types.js').ComputationSignal<V>} **/ (signal), false);
872+
if (DEV) {
873+
// we want to avoid tracking indirect dependencies
874+
const previous_inspect_fn = inspect_fn;
875+
inspect_fn = null;
876+
update_derived(/** @type {import('./types.js').ComputationSignal<V>} **/ (signal), false);
877+
inspect_fn = previous_inspect_fn;
878+
} else {
879+
update_derived(/** @type {import('./types.js').ComputationSignal<V>} **/ (signal), false);
880+
}
840881
}
841882
return signal.v;
842883
}

packages/svelte/src/main/ambient.d.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,23 @@ declare namespace $effect {
132132
declare function $props<T>(): T;
133133

134134
/**
135-
* Logs the arguments whenever they, or the properties they contain, change. Example:
135+
* Inspects a value whenever it, or the properties it contains, change. Example:
136136
*
137137
* ```ts
138-
* $inspect(someValue, someOtherValue)
138+
* $inspect({ someValue, someOtherValue })
139+
* ```
140+
*
141+
* If a second argument is provided, it will be called with the value and the event type
142+
* (`'init'` or `'update'`), otherwise the value will be logged to the console.
143+
*
144+
* ```ts
145+
* $inspect(x, console.trace);
146+
* $inspect(y, (y) => { debugger; });
139147
* ```
140148
*
141149
* https://svelte-5-preview.vercel.app/docs/runes#$inspect
142150
*/
143-
declare function $inspect(): void;
151+
declare function $inspect<T>(
152+
value: T,
153+
callback?: (value: T, type: 'init' | 'update') => void
154+
): void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { test } from '../../test';
2+
3+
/**
4+
* @type {any[]}
5+
*/
6+
let log;
7+
8+
export default test({
9+
compileOptions: {
10+
dev: true
11+
},
12+
13+
get props() {
14+
log = [];
15+
return {
16+
push: (/** @type {any} */ ...v) => log.push(...v)
17+
};
18+
},
19+
20+
async test({ assert, target }) {
21+
const button = target.querySelector('button');
22+
23+
button?.click();
24+
await Promise.resolve();
25+
26+
button?.click();
27+
await Promise.resolve();
28+
29+
assert.deepEqual(log, ['X', 'init', 'XX', 'update', 'XXX', 'update']);
30+
}
31+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
/** @type {{ push: (v: any) => void }} */
3+
let { push } = $props();
4+
5+
let x = $state('x');
6+
let y = $derived(x.toUpperCase());
7+
8+
$inspect(y, push);
9+
</script>
10+
11+
<button on:click={() => x += 'x'}>{x}</button>

0 commit comments

Comments
 (0)