diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 4d09d9293f..6c21717852 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -42,6 +42,9 @@ export function CallExpression(node, context) { e.bindable_invalid_location(node); } + // We need context in case the bound prop is stale + context.state.analysis.needs_context = true; + break; case '$host': diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 6312360a0d..90e1578eef 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -24,6 +24,7 @@ export const EFFECT_HAS_DERIVED = 1 << 20; export const STATE_SYMBOL = Symbol('$state'); export const STATE_SYMBOL_METADATA = Symbol('$state metadata'); export const LEGACY_PROPS = Symbol('legacy props'); +export const TEARDOWN_PROPS = Symbol('teardown props'); export const LOADING_ATTR_SYMBOL = Symbol(''); export const CTX_CONTAINS_TEARDOWN = 1 << 1; diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index 2457a9fa9d..b2c4691fa9 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -13,7 +13,7 @@ import { } from './runtime.js'; import { effect, teardown } from './reactivity/effects.js'; import { legacy_mode_flag } from '../flags/index.js'; -import { CTX_CONTAINS_TEARDOWN, CTX_DESTROYED } from './constants.js'; +import { CTX_CONTAINS_TEARDOWN, CTX_DESTROYED, TEARDOWN_PROPS } from './constants.js'; import { define_property } from '../shared/utils.js'; /** @type {ComponentContext | null} */ @@ -144,6 +144,10 @@ export function push(props, runes = false, fn) { ctx.f ^= CTX_DESTROYED; var teardown_props = ctx.tp; + if (TEARDOWN_PROPS in props) { + props[TEARDOWN_PROPS] = teardown_props; + return; + } // Apply the latest known props before teardown over existing props for (var key in teardown_props) { define_property(props, key, { diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index a105d83d48..532ea82eb1 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -13,7 +13,13 @@ import { derived, derived_safe_equal } from './deriveds.js'; import { get, captured_signals, untrack } from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; -import { CTX_DESTROYED, LEGACY_DERIVED_PROP, LEGACY_PROPS, STATE_SYMBOL } from '../constants.js'; +import { + CTX_DESTROYED, + LEGACY_DERIVED_PROP, + LEGACY_PROPS, + STATE_SYMBOL, + TEARDOWN_PROPS +} from '../constants.js'; import { proxy } from '../proxy.js'; import { capture_store_binding } from './store.js'; import { legacy_mode_flag } from '../../flags/index.js'; @@ -173,6 +179,12 @@ const spread_props_handler = { } }, set(target, key, value) { + // If the spread props have been torn down, then replace the existing props with + // the stale props from the teardown + if (key === TEARDOWN_PROPS) { + target.props = [value]; + return true; + } let i = target.props.length; while (i--) { let p = target.props[i]; @@ -215,6 +227,9 @@ const spread_props_handler = { } }, has(target, key) { + if (key === TEARDOWN_PROPS) { + return true; + } // To prevent a false positive `is_entry_props` in the `prop` function if (key === STATE_SYMBOL || key === LEGACY_PROPS) return false; @@ -368,12 +383,6 @@ export function prop(props, key, flags, fallback) { // source is written to from various places to persist this value. var inner_current_value = mutable_source(prop_value); var current_value = derived(() => { - var ctx = component_context; - - if (ctx !== null && (ctx.f & CTX_DESTROYED) !== 0) { - return get(inner_current_value); - } - var parent_value = getter(); var child_value = get(inner_current_value); @@ -418,6 +427,12 @@ export function prop(props, key, flags, fallback) { return value; } + + // If the prop is read, we might need to return the stale value if component ctx has been destroyed + if (current_value.ctx !== null && (current_value.ctx.f & CTX_DESTROYED) !== 0) { + return current_value.v; + } + return get(current_value); }; } diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/Component.svelte b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/Component.svelte new file mode 100644 index 0000000000..e72a19e946 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/Component.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/_config.js b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/_config.js new file mode 100644 index 0000000000..03a9822cda --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, logs }) { + const [btn1] = target.querySelectorAll('button'); + + btn1.click(); + flushSync(); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/main.svelte new file mode 100644 index 0000000000..85fab11ab1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/main.svelte @@ -0,0 +1,19 @@ + + +{#if state} + {@const attributes = { title: state.title }} + +{/if} + diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js index 794d1f7017..7d1e67e212 100644 --- a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js @@ -61,7 +61,7 @@ export default test({ true, 2, true, - 1, + 2, true ]); }