diff --git a/.changeset/lemon-walls-judge.md b/.changeset/lemon-walls-judge.md new file mode 100644 index 0000000000..cf4f7e6ed1 --- /dev/null +++ b/.changeset/lemon-walls-judge.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: add `isCommitted/isDiscarded/run` to `fork` diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 7823c8e33e..e84a484297 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -372,6 +372,18 @@ export interface Fork { * Discard the fork */ discard(): void; + /** + * Whether the fork has been committed + */ + isCommitted(): boolean; + /** + * Whether the fork has been discarded + */ + isDiscarded(): boolean; + /** + * Run a function within the forked context. + */ + run(fn: () => void): Promise; } export * from './index-client.js'; diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 3b10d6ebe6..7f9c094344 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -1187,6 +1187,37 @@ export function fork(fn) { if (!committed && batches.has(batch)) { batch.discard(); } + }, + isCommitted: () => committed, + isDiscarded: () => !committed && !batches.has(batch), + run: async (fn) => { + if (committed) { + // TODO error instead? + await settled; + fn(); + } else { + // We want to start with the current state of the world, not the + // state back when the fork was created. Also important to + // correctly revert state changes later + const previous = batch.previous; + batch.previous = new Map(); + batch.activate(); + + flushSync(fn); + + // revert state changes + for (var [source, value] of batch.previous) { + source.v = value; + } + + // merge 'previous' map + // TODO is this correct? + for (const [source, value] of previous) { + if (!batch.previous.has(source)) { + batch.previous.set(source, value); + } + } + } } }; } diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 371a823225..2952cf7cd1 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -371,6 +371,18 @@ declare module 'svelte' { * Discard the fork */ discard(): void; + /** + * Whether the fork has been committed + */ + isCommitted(): boolean; + /** + * Whether the fork has been discarded + */ + isDiscarded(): boolean; + /** + * Run a function within the forked context. + */ + run(fn: () => void): Promise; } /** * Returns an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that aborts when the current [derived](https://svelte.dev/docs/svelte/$derived) or [effect](https://svelte.dev/docs/svelte/$effect) re-runs or is destroyed.