diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index a13c040fdd..eb5b78c147 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -17,3 +17,4 @@ export const EFFECT_TRANSPARENT = 1 << 14; export const LEGACY_DERIVED_PROP = 1 << 15; export const STATE_SYMBOL = Symbol('$state'); +export const RAW_SYMBOL = Symbol(); diff --git a/packages/svelte/src/internal/client/dev/equality.js b/packages/svelte/src/internal/client/dev/equality.js new file mode 100644 index 0000000000..218ec33564 --- /dev/null +++ b/packages/svelte/src/internal/client/dev/equality.js @@ -0,0 +1,72 @@ +import { DEV } from 'esm-env'; +import { STATE_SYMBOL } from '../constants'; +import * as w from '../warnings.js'; +import { raw } from '../proxy'; + +export function init_array_prototype_warnings() { + const array_prototype = Array.prototype; + + const original_index_of = array_prototype.indexOf; + + array_prototype.indexOf = function (search_element, from_index) { + const index = original_index_of.call(this, search_element, from_index); + if (index === -1) { + if (original_index_of.call(raw(this), search_element, from_index) !== -1) { + w.state_proxy_equality_mismatch('Array.indexOf'); + } + } + return index; + }; + + const original_last_index_of = array_prototype.lastIndexOf; + + array_prototype.lastIndexOf = function (search_element, from_index) { + const index = original_last_index_of.call(this, search_element, from_index); + if (index === -1) { + if (original_last_index_of.call(raw(this), search_element, from_index) !== -1) { + w.state_proxy_equality_mismatch('Array.lastIndexOf'); + } + } + return index; + }; + + const original_includes = array_prototype.includes; + + array_prototype.includes = function (search_element, from_index) { + const has = original_includes.call(this, search_element, from_index); + if (!has) { + if (original_includes.call(raw(this), search_element, from_index)) { + w.state_proxy_equality_mismatch('Array.includes'); + } + } + return has; + }; +} + +/** + * @param {any} a + * @param {any} b + * @returns {boolean} + */ +export function strict_equals(a, b) { + if (DEV) { + if (a !== b && raw(a) === raw(b)) { + w.state_proxy_equality_mismatch('=== operator'); + } + } + return a === b; +} + +/** + * @param {any} a + * @param {any} b + * @returns {boolean} + */ +export function equals(a, b) { + if (DEV) { + if (a != b && raw(a) == raw(b)) { + w.state_proxy_equality_mismatch('== operator'); + } + } + return a == b; +} diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/select.js b/packages/svelte/src/internal/client/dom/elements/bindings/select.js index f794ae3820..2fcf689f60 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/select.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/select.js @@ -1,7 +1,7 @@ import { effect } from '../../../reactivity/effects.js'; import { listen_to_event_and_reset_event } from './shared.js'; import { untrack } from '../../../runtime.js'; -import { state_is } from '../../../equality.js'; +import { raw } from '../../../proxy.js'; /** * Selects the correct option(s) (depending on whether this is a multiple select) @@ -17,7 +17,7 @@ export function select_option(select, value, mounting) { for (var option of select.options) { var option_value = get_option_value(option); - if (state_is(option_value, value)) { + if (option_value === raw(value)) { option.selected = true; return; } diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 57e93cc274..b5c4d2613a 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -1,7 +1,7 @@ import { hydrate_anchor, hydrate_nodes, hydrating } from './hydration.js'; import { get_descriptor } from '../utils.js'; import { DEV } from 'esm-env'; -import { init_array_prototype_warnings } from '../equality.js'; +import { init_array_prototype_warnings } from '../dev/equality.js'; // We cache the Node and Element prototype methods, so that we can avoid doing // expensive prototype chain lookups. diff --git a/packages/svelte/src/internal/client/equality.js b/packages/svelte/src/internal/client/equality.js deleted file mode 100644 index 791cd24423..0000000000 --- a/packages/svelte/src/internal/client/equality.js +++ /dev/null @@ -1,118 +0,0 @@ -import { DEV } from 'esm-env'; -import { STATE_SYMBOL } from './constants'; -import * as w from './warnings.js'; - -const object_is = Object.is; - -export function init_array_prototype_warnings() { - const array_prototype = Array.prototype; - - const original_index_of = array_prototype.indexOf; - - array_prototype.indexOf = function (search_element, from_index) { - const index = original_index_of.call(this, search_element, from_index); - if ( - index === -1 && - search_element != null && - typeof search_element === 'object' && - STATE_SYMBOL in search_element - ) { - const o = search_element[STATE_SYMBOL]; - if (o != null) { - if (original_index_of.call(this, o.p, from_index) !== -1) { - w.state_proxy_equality_mismatch('Array.indexOf'); - } - } - } - return index; - }; - - const original_last_index_of = array_prototype.lastIndexOf; - - array_prototype.lastIndexOf = function (search_element, from_index) { - const index = original_last_index_of.call(this, search_element, from_index); - if ( - index === -1 && - search_element != null && - typeof search_element === 'object' && - STATE_SYMBOL in search_element - ) { - const o = search_element[STATE_SYMBOL]; - if (o != null) { - if (original_last_index_of.call(this, o.p, from_index) !== -1) { - w.state_proxy_equality_mismatch('Array.lastIndexOf'); - } - } - } - return index; - }; - - const original_includes = array_prototype.includes; - - array_prototype.includes = function (search_element, from_index) { - const has = original_includes.call(this, search_element, from_index); - if ( - has && - search_element != null && - typeof search_element === 'object' && - STATE_SYMBOL in search_element - ) { - const o = search_element[STATE_SYMBOL]; - if (o != null) { - if (original_includes.call(this, o.p, from_index)) { - w.state_proxy_equality_mismatch('Array.includes'); - } - } - } - return has; - }; -} - -/** - * @param {any} a - * @param {any} b - * @returns {boolean} - */ -export function state_is(a, b) { - if (a != null && typeof a === 'object' && STATE_SYMBOL in a) { - const o = a[STATE_SYMBOL]; - if (o != null && object_is(o.p, b)) { - return true; - } - } - if (b != null && typeof b === 'object' && STATE_SYMBOL in b) { - const o = b[STATE_SYMBOL]; - if (o != null) { - return object_is(o.p, a); - } - } - return object_is(a, b); -} - -/** - * @param {any} a - * @param {any} b - * @returns {boolean} - */ -export function strict_equals(a, b) { - if (DEV) { - if (state_is(a, b)) { - w.state_proxy_equality_mismatch('=== operator'); - } - } - return a === b; -} - -/** - * @param {any} a - * @param {any} b - * @returns {boolean} - */ -export function equals(a, b) { - if (DEV) { - if (state_is(a, b)) { - w.state_proxy_equality_mismatch('== operator'); - } - } - return a == b; -} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 90eb53742c..e1294b89ae 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -161,4 +161,4 @@ export { validate_store, validate_void_dynamic_element } from '../shared/validate.js'; -export { strict_equals, equals } from './equality.js'; +export { strict_equals, equals } from './dev/equality.js'; diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index e845da3ba2..d52609a8c5 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -18,7 +18,7 @@ import { } from './utils.js'; import { check_ownership, widen_ownership } from './dev/ownership.js'; import { mutable_source, source, set } from './reactivity/sources.js'; -import { STATE_SYMBOL } from './constants.js'; +import { RAW_SYMBOL, STATE_SYMBOL } from './constants.js'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; @@ -199,6 +199,9 @@ const state_proxy_handler = { if (prop === STATE_SYMBOL) { return Reflect.get(target, STATE_SYMBOL); } + if (prop === RAW_SYMBOL) { + return target; + } /** @type {import('#client').ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; @@ -337,3 +340,16 @@ if (DEV) { e.state_prototype_fixed(); }; } + +/** + * @param {any} x + */ +export function raw(x) { + if (x !== null && typeof x === 'object' && STATE_SYMBOL in x) { + var raw_value = x[RAW_SYMBOL]; + if (raw_value !== undefined) { + return raw_value; + } + } + return x; +}