From f21163a5ae63c3f928b73a82e2281a5a4c064964 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 15 Jul 2024 09:34:52 +0200 Subject: [PATCH] fix: ensure `$state.snapshot` never errors Snapshotting can error on un-cloneable objects. It's not practical to error in this case; often there's no way out of this for users, so it makes sense to return the original value in that case, and warn in dev mode about it. closes #12438 --- .changeset/brave-gorillas-fold.md | 5 +++++ .../svelte/messages/shared-warnings/warnings.md | 4 ++++ packages/svelte/src/internal/shared/clone.js | 17 ++++++++++++++++- packages/svelte/src/internal/shared/warnings.js | 12 ++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .changeset/brave-gorillas-fold.md diff --git a/.changeset/brave-gorillas-fold.md b/.changeset/brave-gorillas-fold.md new file mode 100644 index 0000000000..5a551cbff2 --- /dev/null +++ b/.changeset/brave-gorillas-fold.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure `$state.snapshot` never errors diff --git a/packages/svelte/messages/shared-warnings/warnings.md b/packages/svelte/messages/shared-warnings/warnings.md index 8154436843..ee5b32c9f4 100644 --- a/packages/svelte/messages/shared-warnings/warnings.md +++ b/packages/svelte/messages/shared-warnings/warnings.md @@ -1,3 +1,7 @@ ## dynamic_void_element_content > `` is a void element — it cannot have content + +## state_snapshot_uncloneable + +> An object could not be cloned with $state.snapshot, the original value will be returned diff --git a/packages/svelte/src/internal/shared/clone.js b/packages/svelte/src/internal/shared/clone.js index 715ec3d855..51dee7da47 100644 --- a/packages/svelte/src/internal/shared/clone.js +++ b/packages/svelte/src/internal/shared/clone.js @@ -1,4 +1,6 @@ /** @import { Snapshot } from './types' */ +import { DEV } from 'esm-env'; +import * as w from './warnings.js'; import { get_prototype_of, is_array, object_prototype } from './utils.js'; /** @@ -50,5 +52,18 @@ function clone(value, cloned) { } } - return /** @type {Snapshot} */ (structuredClone(value)); + if (value instanceof EventTarget) { + // can't be cloned + return /** @type {Snapshot} */ (value); + } + + try { + return /** @type {Snapshot} */ (structuredClone(value)); + } catch (e) { + if (DEV) { + w.state_snapshot_uncloneable(); + console.warn(e); + } + return /** @type {Snapshot} */ (value); + } } diff --git a/packages/svelte/src/internal/shared/warnings.js b/packages/svelte/src/internal/shared/warnings.js index 2fc1b6fdd2..bef3b05c4f 100644 --- a/packages/svelte/src/internal/shared/warnings.js +++ b/packages/svelte/src/internal/shared/warnings.js @@ -16,4 +16,16 @@ export function dynamic_void_element_content(tag) { // TODO print a link to the documentation console.warn("dynamic_void_element_content"); } +} + +/** + * An object could not be cloned with $state.snapshot, the original value will be returned + */ +export function state_snapshot_uncloneable() { + if (DEV) { + console.warn(`%c[svelte] state_snapshot_uncloneable\n%cAn object could not be cloned with $state.snapshot, the original value will be returned`, bold, normal); + } else { + // TODO print a link to the documentation + console.warn("state_snapshot_uncloneable"); + } } \ No newline at end of file