From 873a184b411b669c4a854799a7282be4a187c93a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 14 Aug 2024 13:04:45 +0100 Subject: [PATCH] fix: ensure each key validation occurs for updates (#12836) * fix: ensure each key validation occurs for updates * fix: ensure each key validation occurs for updates --- .changeset/tame-dodos-float.md | 5 +++ .../svelte/src/internal/client/validate.js | 44 ++++++++++--------- .../keyed-each-dev-unique-update/_config.js | 16 +++++++ .../keyed-each-dev-unique-update/main.svelte | 21 +++++++++ 4 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 .changeset/tame-dodos-float.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/main.svelte diff --git a/.changeset/tame-dodos-float.md b/.changeset/tame-dodos-float.md new file mode 100644 index 0000000000..4ba67a952a --- /dev/null +++ b/.changeset/tame-dodos-float.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure each key validation occurs for updates diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index 415982a02b..8792d02f0d 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -45,28 +45,30 @@ export function validate_dynamic_component(component_fn) { * @returns {void} */ export function validate_each_keys(collection, key_fn) { - const keys = new Map(); - const maybe_array = untrack(() => collection()); - const array = is_array(maybe_array) - ? maybe_array - : maybe_array == null - ? [] - : Array.from(maybe_array); - const length = array.length; - for (let i = 0; i < length; i++) { - const key = key_fn(array[i], i); - if (keys.has(key)) { - const a = String(keys.get(key)); - const b = String(i); - - /** @type {string | null} */ - let k = String(array[i]); - if (k.startsWith('[object ')) k = null; - - e.each_key_duplicate(a, b, k); + render_effect(() => { + const keys = new Map(); + const maybe_array = collection(); + const array = is_array(maybe_array) + ? maybe_array + : maybe_array == null + ? [] + : Array.from(maybe_array); + const length = array.length; + for (let i = 0; i < length; i++) { + const key = key_fn(array[i], i); + if (keys.has(key)) { + const a = String(keys.get(key)); + const b = String(i); + + /** @type {string | null} */ + let k = String(array[i]); + if (k.startsWith('[object ')) k = null; + + e.each_key_duplicate(a, b, k); + } + keys.set(key, i); } - keys.set(key, i); - } + }); } /** diff --git a/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/_config.js b/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/_config.js new file mode 100644 index 0000000000..6fa6f63b0a --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + test({ assert, target }) { + let button = target.querySelector('button'); + + button?.click(); + + assert.throws(flushSync, /each_key_duplicate/); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/main.svelte b/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/main.svelte new file mode 100644 index 0000000000..0e9a377487 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique-update/main.svelte @@ -0,0 +1,21 @@ + + + + +