Skip to content

Commit 8e5f1db

Browse files
committed
fix: don't clone non-proxies in $inspect
1 parent 2e02868 commit 8e5f1db

File tree

3 files changed

+58
-19
lines changed

3 files changed

+58
-19
lines changed

.changeset/itchy-games-guess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: don't clone non-proxies in `$inspect`

packages/svelte/src/internal/client/dev/inspect.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function inspect(get_value, inspector = console.log) {
2626
return;
2727
}
2828

29-
var snap = snapshot(value, true);
29+
var snap = snapshot(value, true, true);
3030
untrack(() => {
3131
inspector(initial ? 'init' : 'update', ...snap);
3232
});

packages/svelte/src/internal/shared/clone.js

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ const empty = [];
1515
* @template T
1616
* @param {T} value
1717
* @param {boolean} [skip_warning]
18+
* @param {boolean} [only_deproxy]
1819
* @returns {Snapshot<T>}
1920
*/
20-
export function snapshot(value, skip_warning = false) {
21+
export function snapshot(value, skip_warning = false, only_deproxy = false) {
2122
if (DEV && !skip_warning) {
2223
/** @type {string[]} */
2324
const paths = [];
@@ -40,7 +41,7 @@ export function snapshot(value, skip_warning = false) {
4041
return copy;
4142
}
4243

43-
return clone(value, new Map(), '', empty);
44+
return clone(value, new Map(), '', empty, null, only_deproxy);
4445
}
4546

4647
/**
@@ -49,16 +50,19 @@ export function snapshot(value, skip_warning = false) {
4950
* @param {Map<T, Snapshot<T>>} cloned
5051
* @param {string} path
5152
* @param {string[]} paths
52-
* @param {null | T} original The original value, if `value` was produced from a `toJSON` call
53+
* @param {null | T} [original] The original value, if `value` was produced from a `toJSON` call
54+
* @param {boolean} [only_deproxy] Don't clone objects that aren't proxies
5355
* @returns {Snapshot<T>}
5456
*/
55-
function clone(value, cloned, path, paths, original = null) {
57+
function clone(value, cloned, path, paths, original = null, only_deproxy = false) {
5658
if (typeof value === 'object' && value !== null) {
5759
var unwrapped = cloned.get(value);
5860
if (unwrapped !== undefined) return unwrapped;
5961

60-
if (value instanceof Map) return /** @type {Snapshot<T>} */ (new Map(value));
61-
if (value instanceof Set) return /** @type {Snapshot<T>} */ (new Set(value));
62+
if (value instanceof Map)
63+
return /** @type {Snapshot<T>} */ (only_deproxy ? value : new Map(value));
64+
if (value instanceof Set)
65+
return /** @type {Snapshot<T>} */ (only_deproxy ? value : new Set(value));
6266

6367
if (is_array(value)) {
6468
var copy = /** @type {Snapshot<any>} */ (Array(value.length));
@@ -71,7 +75,7 @@ function clone(value, cloned, path, paths, original = null) {
7175
for (var i = 0; i < value.length; i += 1) {
7276
var element = value[i];
7377
if (i in value) {
74-
copy[i] = clone(element, cloned, DEV ? `${path}[${i}]` : path, paths);
78+
copy[i] = clone(element, cloned, DEV ? `${path}[${i}]` : path, paths, null, only_deproxy);
7579
}
7680
}
7781

@@ -88,26 +92,49 @@ function clone(value, cloned, path, paths, original = null) {
8892
}
8993

9094
for (var key in value) {
91-
// @ts-expect-error
92-
copy[key] = clone(value[key], cloned, DEV ? `${path}.${key}` : path, paths);
95+
copy[key] = clone(
96+
// @ts-expect-error
97+
value[key],
98+
cloned,
99+
DEV ? `${path}.${key}` : path,
100+
paths,
101+
null,
102+
only_deproxy
103+
);
93104
}
94105

95106
return copy;
96107
}
97108

98109
if (value instanceof Date) {
99-
return /** @type {Snapshot<T>} */ (structuredClone(value));
110+
if (only_deproxy) {
111+
structuredClone(value);
112+
} else {
113+
return /** @type {Snapshot<T>} */ (structuredClone(value));
114+
}
100115
}
101116

102117
if (typeof (/** @type {T & { toJSON?: any } } */ (value).toJSON) === 'function') {
103-
return clone(
104-
/** @type {T & { toJSON(): any } } */ (value).toJSON(),
105-
cloned,
106-
DEV ? `${path}.toJSON()` : path,
107-
paths,
108-
// Associate the instance with the toJSON clone
109-
value
110-
);
118+
if (!only_deproxy) {
119+
return clone(
120+
/** @type {T & { toJSON(): any } } */ (value).toJSON(),
121+
cloned,
122+
DEV ? `${path}.toJSON()` : path,
123+
paths,
124+
// Associate the instance with the toJSON clone
125+
value
126+
);
127+
} else {
128+
// we still want to read each property
129+
clone(
130+
/** @type {T & { toJSON(): any } } */ (value).toJSON(),
131+
cloned,
132+
DEV ? `${path}.toJSON()` : path,
133+
paths,
134+
// Associate the instance with the toJSON clone
135+
value
136+
);
137+
}
111138
}
112139
}
113140

@@ -116,6 +143,13 @@ function clone(value, cloned, path, paths, original = null) {
116143
return /** @type {Snapshot<T>} */ (value);
117144
}
118145

146+
if (only_deproxy) {
147+
try {
148+
structuredClone(value);
149+
} catch {}
150+
return /** @type {Snapshot<T>} */ (value);
151+
}
152+
119153
try {
120154
return /** @type {Snapshot<T>} */ (structuredClone(value));
121155
} catch (e) {

0 commit comments

Comments
 (0)