diff --git a/.changeset/cool-trains-yawn.md b/.changeset/cool-trains-yawn.md new file mode 100644 index 0000000000..d5198d118c --- /dev/null +++ b/.changeset/cool-trains-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure internal cloning can work circular values diff --git a/packages/svelte/src/internal/shared/clone.js b/packages/svelte/src/internal/shared/clone.js index bfdc9af263..40a9f0724c 100644 --- a/packages/svelte/src/internal/shared/clone.js +++ b/packages/svelte/src/internal/shared/clone.js @@ -64,7 +64,11 @@ function clone(value, cloned, path, paths) { cloned.set(value, copy); for (let i = 0; i < value.length; i += 1) { - copy.push(clone(value[i], cloned, DEV ? `${path}[${i}]` : path, paths)); + var element = value[i]; + if (cloned.get(element) === null) { + cloned.set(element, copy); + } + copy.push(clone(element, cloned, DEV ? `${path}[${i}]` : path, paths)); } return copy; @@ -76,8 +80,11 @@ function clone(value, cloned, path, paths) { cloned.set(value, copy); for (var key in value) { - // @ts-expect-error - copy[key] = clone(value[key], cloned, DEV ? `${path}.${key}` : path, paths); + var prop = /** @type {any} */ (value[key]); + if (cloned.get(prop) === null) { + cloned.set(prop, copy); + } + copy[key] = clone(prop, cloned, DEV ? `${path}.${key}` : path, paths); } return copy; @@ -88,6 +95,10 @@ function clone(value, cloned, path, paths) { } if (typeof (/** @type {T & { toJSON?: any } } */ (value).toJSON) === 'function') { + // To avoid cycles set the clone to null, so if we encounter it again later we can + // slot in the currently copied object instead + // @ts-ignore + cloned.set(value, null); return clone( /** @type {T & { toJSON(): any } } */ (value).toJSON(), cloned, diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js new file mode 100644 index 0000000000..bfa47ecb3f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js @@ -0,0 +1,15 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + async test({ assert, logs }) { + var a = { + a: null + }; + a.a = a; + assert.deepEqual(logs, ['init', { a }]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte new file mode 100644 index 0000000000..cd7eb8afdd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte @@ -0,0 +1,11 @@ +