From 657ec89caa93d39d2cf8d8cdbe27b0041615409f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Oct 2025 14:12:22 -0400 Subject: [PATCH] fix: ignore fork `discard()` after `commit()` (#17034) * fix: ignore fork `discard()` after `commit()` * fix message --- .changeset/twenty-onions-attack.md | 5 +++++ .../98-reference/.generated/client-errors.md | 2 +- .../svelte/messages/client-errors/errors.md | 2 +- packages/svelte/src/internal/client/errors.js | 4 ++-- .../src/internal/client/reactivity/batch.js | 22 +++++++++++++------ 5 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 .changeset/twenty-onions-attack.md diff --git a/.changeset/twenty-onions-attack.md b/.changeset/twenty-onions-attack.md new file mode 100644 index 0000000000..c23c8b590b --- /dev/null +++ b/.changeset/twenty-onions-attack.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ignore fork `discard()` after `commit()` diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 74a0674dba..3f1cb8f76b 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -149,7 +149,7 @@ This restriction only applies when using the `experimental.async` option, which ### fork_discarded ``` -Cannot commit a fork that was already committed or discarded +Cannot commit a fork that was already discarded ``` ### fork_timing diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index b5fe51539d..ae7d811b2e 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -114,7 +114,7 @@ This restriction only applies when using the `experimental.async` option, which ## fork_discarded -> Cannot commit a fork that was already committed or discarded +> Cannot commit a fork that was already discarded ## fork_timing diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 2a433ed8f9..8a5fde4f3b 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -262,12 +262,12 @@ export function flush_sync_in_effect() { } /** - * Cannot commit a fork that was already committed or discarded + * Cannot commit a fork that was already discarded * @returns {never} */ export function fork_discarded() { if (DEV) { - const error = new Error(`fork_discarded\nCannot commit a fork that was already committed or discarded\nhttps://svelte.dev/e/fork_discarded`); + const error = new Error(`fork_discarded\nCannot commit a fork that was already discarded\nhttps://svelte.dev/e/fork_discarded`); error.name = 'Svelte error'; diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index fdeb111a4d..ab83050cd0 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -913,28 +913,36 @@ export function fork(fn) { e.fork_timing(); } - const batch = Batch.ensure(); + var batch = Batch.ensure(); batch.is_fork = true; - const settled = batch.settled(); + var committed = false; + var settled = batch.settled(); flushSync(fn); // revert state changes - for (const [source, value] of batch.previous) { + for (var [source, value] of batch.previous) { source.v = value; } return { commit: async () => { + if (committed) { + await settled; + return; + } + if (!batches.has(batch)) { e.fork_discarded(); } + committed = true; + batch.is_fork = false; // apply changes - for (const [source, value] of batch.current) { + for (var [source, value] of batch.current) { source.v = value; } @@ -945,9 +953,9 @@ export function fork(fn) { // TODO maybe there's a better implementation? flushSync(() => { /** @type {Set} */ - const eager_effects = new Set(); + var eager_effects = new Set(); - for (const source of batch.current.keys()) { + for (var source of batch.current.keys()) { mark_eager_effects(source, eager_effects); } @@ -959,7 +967,7 @@ export function fork(fn) { await settled; }, discard: () => { - if (batches.has(batch)) { + if (!committed && batches.has(batch)) { batches.delete(batch); batch.discard(); }