From b20b4617c05ef6d3391ed94123096044d949e486 Mon Sep 17 00:00:00 2001
From: Rich Harris <richard.a.harris@gmail.com>
Date: Thu, 7 Dec 2023 05:18:51 -0500
Subject: [PATCH] chore: rethink props (#9826)

Cleaned up prop_source and renamed it to prop. Updated tests accordingly
---
 .changeset/empty-crabs-think.md               |   5 +
 .../3-transform/client/transform-client.js    |   4 +-
 .../phases/3-transform/client/utils.js        |  65 +++--
 .../3-transform/client/visitors/global.js     |  21 +-
 .../client/visitors/javascript-legacy.js      |  10 +-
 .../client/visitors/javascript-runes.js       |   2 +-
 packages/svelte/src/constants.js              |   3 +-
 .../svelte/src/internal/client/proxy/proxy.js |   6 +-
 packages/svelte/src/internal/client/render.js |   2 +-
 .../svelte/src/internal/client/runtime.js     | 255 ++++++++----------
 packages/svelte/src/internal/index.js         |  16 +-
 .../samples/binding-backflow/Child.svelte     |   2 +-
 .../samples/binding-backflow/_config.js       |   4 +-
 .../samples/binding-indirect/_config.js       |   2 +-
 .../_config.js                                |   2 +-
 .../binding-input-checkbox-group/_config.js   |   2 +-
 .../binding-input-group-each-1/_config.js     |   2 +-
 .../binding-input-group-each-3/_config.js     |   2 +-
 .../binding-input-radio-group/_config.js      |   2 +-
 .../_config.js                                |   2 +-
 .../_config.js                                |   2 +-
 .../dynamic-component-dirty/_config.js        |   4 +-
 .../select-one-way-bind-object/_config.js     |   4 +-
 .../samples/derived-stale-value/_config.js    |   9 +-
 .../samples/derived-stale-value/log.js        |   2 +
 .../samples/derived-stale-value/main.svelte   |   4 +-
 .../samples/effect-cleanup/_config.js         |   9 +-
 .../samples/effect-cleanup/log.js             |   2 +
 .../samples/effect-cleanup/main.svelte        |   2 +-
 .../samples/effect-order/_config.js           |   9 +-
 .../runtime-runes/samples/effect-order/log.js |   2 +
 .../samples/effect-order/main.svelte          |   2 +-
 .../samples/effect-root/_config.js            |  13 +-
 .../runtime-runes/samples/effect-root/log.js  |   2 +
 .../samples/effect-root/main.svelte           |   2 +-
 .../runtime-runes/samples/effect/_config.js   |   9 +-
 .../tests/runtime-runes/samples/effect/log.js |   2 +
 .../runtime-runes/samples/effect/main.svelte  |   2 +-
 .../samples/effects-order/_config.js          |   9 +-
 .../samples/effects-order/log.js              |   2 +
 .../samples/effects-order/main.svelte         |   2 +-
 .../Item.svelte                               |  12 +-
 .../_config.js                                |  25 +-
 .../log.js                                    |   2 +
 .../main.svelte                               |  13 +-
 .../Item.svelte                               |  10 +-
 .../_config.js                                |  25 +-
 .../log.js                                    |   2 +
 .../main.svelte                               |   9 +-
 .../Item.svelte                               |  12 +-
 .../_config.js                                |  25 +-
 .../log.js                                    |   2 +
 .../main.svelte                               |  13 +-
 .../Item.svelte                               |  10 +-
 .../_config.js                                |  25 +-
 .../log.js                                    |   2 +
 .../main.svelte                               |  11 +-
 .../samples/nullish-operator/_config.js       |   9 +-
 .../samples/nullish-operator/log.js           |   2 +
 .../samples/nullish-operator/main.svelte      |   5 +-
 .../samples/pre-effect-ordering/_config.js    |   9 +-
 .../samples/pre-effect-ordering/log.js        |   2 +
 .../samples/pre-effect-ordering/main.svelte   |   7 +-
 .../samples/pre-effect/_config.js             |   9 +-
 .../runtime-runes/samples/pre-effect/log.js   |   2 +
 .../samples/pre-effect/main.svelte            |   2 +-
 .../_expected/client/index.svelte.js          |   6 +-
 67 files changed, 395 insertions(+), 359 deletions(-)
 create mode 100644 .changeset/empty-crabs-think.md
 create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-stale-value/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-cleanup/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-order/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/effect/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/effects-order/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/nullish-operator/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/log.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/pre-effect/log.js

diff --git a/.changeset/empty-crabs-think.md b/.changeset/empty-crabs-think.md
new file mode 100644
index 0000000000..9e42b23228
--- /dev/null
+++ b/.changeset/empty-crabs-think.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+chore: refactor props handling
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
index d69b70ec08..d82cdd68f2 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
@@ -255,8 +255,8 @@ export function client_component(source, analysis, options) {
 			const key = binding.prop_alias ?? name;
 
 			properties.push(
-				b.get(key, [b.return(b.call('$.get', b.id(name)))]),
-				b.set(key, [b.stmt(b.call('$.set_sync', b.id(name), b.id('$$value')))])
+				b.get(key, [b.return(b.call(b.id(name)))]),
+				b.set(key, [b.stmt(b.call(b.id(name), b.id('$$value'))), b.stmt(b.call('$.flushSync'))])
 			);
 		}
 	}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js
index 20cec7a7c1..90f5c6ec29 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js
@@ -2,9 +2,10 @@ import * as b from '../../../utils/builders.js';
 import { extract_paths, is_simple_expression } from '../../../utils/ast.js';
 import { error } from '../../../errors.js';
 import {
-	PROPS_CALL_DEFAULT_VALUE,
+	PROPS_IS_LAZY_INITIAL,
 	PROPS_IS_IMMUTABLE,
-	PROPS_IS_RUNES
+	PROPS_IS_RUNES,
+	PROPS_IS_UPDATED
 } from '../../../../constants.js';
 
 /**
@@ -73,12 +74,14 @@ export function serialize_get_binding(node, state) {
 		}
 
 		if (
-			!state.analysis.accessors &&
-			!(state.analysis.immutable ? binding.reassigned : binding.mutated) &&
-			!binding.initial
+			state.analysis.accessors ||
+			(state.analysis.immutable ? binding.reassigned : binding.mutated) ||
+			binding.initial
 		) {
-			return b.member(b.id('$$props'), node);
+			return b.call(node);
 		}
+
+		return b.member(b.id('$$props'), node);
 	}
 
 	if (binding.kind === 'legacy_reactive_import') {
@@ -89,7 +92,6 @@ export function serialize_get_binding(node, state) {
 		(binding.kind === 'state' &&
 			(!state.analysis.immutable || state.analysis.accessors || binding.reassigned)) ||
 		binding.kind === 'derived' ||
-		binding.kind === 'prop' ||
 		binding.kind === 'legacy_reactive'
 	) {
 		return b.call('$.get', node);
@@ -208,9 +210,12 @@ export function serialize_set_binding(node, context, fallback) {
 	}
 
 	const value = get_assignment_value(node, context);
+
 	const serialize = () => {
 		if (left === node.left) {
-			if (is_store) {
+			if (binding.kind === 'prop') {
+				return b.call(left, value);
+			} else if (is_store) {
 				return b.call('$.store_set', serialize_get_binding(b.id(left_name), state), value);
 			} else {
 				return b.call(
@@ -232,15 +237,27 @@ export function serialize_set_binding(node, context, fallback) {
 					b.call('$' + left_name)
 				);
 			} else if (!state.analysis.runes) {
-				return b.call(
-					'$.mutate',
-					b.id(left_name),
-					b.assignment(
-						node.operator,
-						/** @type {import('estree').Pattern} */ (visit(node.left)),
-						value
-					)
-				);
+				if (binding.kind === 'prop') {
+					return b.call(
+						left,
+						b.assignment(
+							node.operator,
+							/** @type {import('estree').Pattern} */ (visit(node.left)),
+							value
+						),
+						b.literal(true)
+					);
+				} else {
+					return b.call(
+						'$.mutate',
+						b.id(left_name),
+						b.assignment(
+							node.operator,
+							/** @type {import('estree').Pattern} */ (visit(node.left)),
+							value
+						)
+					);
+				}
 			} else {
 				return b.assignment(
 					node.operator,
@@ -345,12 +362,13 @@ export function serialize_hoistable_params(node, context) {
 }
 
 /**
+ * @param {import('#compiler').Binding} binding
  * @param {import('./types').ComponentClientTransformState} state
  * @param {string} name
  * @param {import('estree').Expression | null} [initial]
  * @returns
  */
-export function get_prop_source(state, name, initial) {
+export function get_prop_source(binding, state, name, initial) {
 	/** @type {import('estree').Expression[]} */
 	const args = [b.id('$$props'), b.literal(name)];
 
@@ -364,6 +382,13 @@ export function get_prop_source(state, name, initial) {
 		flags |= PROPS_IS_RUNES;
 	}
 
+	if (
+		state.analysis.accessors ||
+		(state.analysis.immutable ? binding.reassigned : binding.mutated)
+	) {
+		flags |= PROPS_IS_UPDATED;
+	}
+
 	/** @type {import('estree').Expression | undefined} */
 	let arg;
 
@@ -382,7 +407,7 @@ export function get_prop_source(state, name, initial) {
 				arg = b.thunk(initial);
 			}
 
-			flags |= PROPS_CALL_DEFAULT_VALUE;
+			flags |= PROPS_IS_LAZY_INITIAL;
 		}
 	}
 
@@ -391,7 +416,7 @@ export function get_prop_source(state, name, initial) {
 		if (arg) args.push(arg);
 	}
 
-	return b.call('$.prop_source', ...args);
+	return b.call('$.prop', ...args);
 }
 
 /**
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js
index 51032cdd5f..136bb020a9 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js
@@ -45,6 +45,7 @@ export const global_visitors = {
 			const binding = state.scope.get(argument.name);
 			const is_store = binding?.kind === 'store_sub';
 			const name = is_store ? argument.name.slice(1) : argument.name;
+
 			// use runtime functions for smaller output
 			if (
 				binding?.kind === 'state' ||
@@ -53,18 +54,28 @@ export const global_visitors = {
 				binding?.kind === 'prop' ||
 				is_store
 			) {
-				let fn = node.operator === '++' ? '$.increment' : '$.decrement';
+				/** @type {import('estree').Expression[]} */
+				const args = [];
+
+				let fn = '$.update';
 				if (node.prefix) fn += '_pre';
 
 				if (is_store) {
 					fn += '_store';
-					return b.call(fn, serialize_get_binding(b.id(name), state), b.call('$' + name));
+					args.push(serialize_get_binding(b.id(name), state), b.call('$' + name));
 				} else {
-					return b.call(fn, b.id(name));
+					if (binding.kind === 'prop') fn += '_prop';
+					args.push(b.id(name));
 				}
-			} else {
-				return next();
+
+				if (node.operator === '--') {
+					args.push(b.literal(-1));
+				}
+
+				return b.call(fn, ...args);
 			}
+
+			return next();
 		} else if (
 			argument.type === 'MemberExpression' &&
 			argument.object.type === 'ThisExpression' &&
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js
index ab2e4e3aaf..7471fb860c 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js
@@ -55,7 +55,7 @@ export const javascript_visitors_legacy = {
 							b.declarator(
 								path.node,
 								binding.kind === 'prop'
-									? get_prop_source(state, binding.prop_alias ?? name, value)
+									? get_prop_source(binding, state, binding.prop_alias ?? name, value)
 									: value
 							)
 						);
@@ -76,6 +76,7 @@ export const javascript_visitors_legacy = {
 						b.declarator(
 							declarator.id,
 							get_prop_source(
+								binding,
 								state,
 								binding.prop_alias ?? declarator.id.name,
 								declarator.init &&
@@ -107,8 +108,11 @@ export const javascript_visitors_legacy = {
 		};
 	},
 	LabeledStatement(node, context) {
-		if (context.path.length > 1) return;
-		if (node.label.name !== '$') return;
+		if (context.path.length > 1 || node.label.name !== '$') {
+			context.next();
+			return;
+		}
+
 		const state = context.state;
 		// To recreate Svelte 4 behaviour, we track the dependencies
 		// the compiler can 'see', but we untrack the effect itself
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js
index 3bc15943b5..489063085f 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js
@@ -185,7 +185,7 @@ export const javascript_visitors_runes = {
 						const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name));
 
 						if (binding.reassigned || state.analysis.accessors || initial) {
-							declarations.push(b.declarator(id, get_prop_source(state, name, initial)));
+							declarations.push(b.declarator(id, get_prop_source(binding, state, name, initial)));
 						}
 					} else {
 						// RestElement
diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js
index f22a36f45f..3df03ded08 100644
--- a/packages/svelte/src/constants.js
+++ b/packages/svelte/src/constants.js
@@ -7,7 +7,8 @@ export const EACH_IS_IMMUTABLE = 1 << 6;
 
 export const PROPS_IS_IMMUTABLE = 1;
 export const PROPS_IS_RUNES = 1 << 1;
-export const PROPS_CALL_DEFAULT_VALUE = 1 << 2;
+export const PROPS_IS_UPDATED = 1 << 2;
+export const PROPS_IS_LAZY_INITIAL = 1 << 3;
 
 /** List of Element events that will be delegated */
 export const DelegatedEvents = [
diff --git a/packages/svelte/src/internal/client/proxy/proxy.js b/packages/svelte/src/internal/client/proxy/proxy.js
index a83b444cc2..eb392749e0 100644
--- a/packages/svelte/src/internal/client/proxy/proxy.js
+++ b/packages/svelte/src/internal/client/proxy/proxy.js
@@ -3,7 +3,7 @@ import {
 	effect_active,
 	get,
 	set,
-	increment,
+	update,
 	source,
 	updating_derived,
 	UNINITIALIZED,
@@ -143,7 +143,7 @@ const handler = {
 		const s = metadata.s.get(prop);
 		if (s !== undefined) set(s, UNINITIALIZED);
 
-		if (prop in target) increment(metadata.v);
+		if (prop in target) update(metadata.v);
 
 		return delete target[prop];
 	},
@@ -224,7 +224,7 @@ const handler = {
 			}
 		}
 		if (not_has) {
-			increment(metadata.v);
+			update(metadata.v);
 		}
 		// @ts-ignore
 		target[prop] = value;
diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js
index 30a21d9158..c4161018dc 100644
--- a/packages/svelte/src/internal/client/render.js
+++ b/packages/svelte/src/internal/client/render.js
@@ -2658,7 +2658,7 @@ export function createRoot(component, options) {
 			/** @param {any} value */
 			set(value) {
 				// @ts-expect-error TS doesn't know key exists on accessor
-				accessors[key] = value;
+				flushSync(() => (accessors[key] = value));
 			},
 			enumerable: true
 		});
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 1d5630d1d2..5f97f3e555 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -2,16 +2,20 @@ import { DEV } from 'esm-env';
 import { subscribe_to_store } from '../../store/utils.js';
 import { EMPTY_FUNC, run_all } from '../common.js';
 import { get_descriptor, get_descriptors, is_array } from './utils.js';
-import { PROPS_CALL_DEFAULT_VALUE, PROPS_IS_IMMUTABLE, PROPS_IS_RUNES } from '../../constants.js';
+import {
+	PROPS_IS_LAZY_INITIAL,
+	PROPS_IS_IMMUTABLE,
+	PROPS_IS_RUNES,
+	PROPS_IS_UPDATED
+} from '../../constants.js';
 import { readonly } from './proxy/readonly.js';
-import { observe, proxy } from './proxy/proxy.js';
+import { proxy } from './proxy/proxy.js';
 
 export const SOURCE = 1;
 export const DERIVED = 1 << 1;
 export const EFFECT = 1 << 2;
 export const PRE_EFFECT = 1 << 3;
 export const RENDER_EFFECT = 1 << 4;
-export const SYNC_EFFECT = 1 << 5;
 const MANAGED = 1 << 6;
 const UNOWNED = 1 << 7;
 export const CLEAN = 1 << 8;
@@ -20,7 +24,7 @@ export const MAYBE_DIRTY = 1 << 10;
 export const INERT = 1 << 11;
 export const DESTROYED = 1 << 12;
 
-const IS_EFFECT = EFFECT | PRE_EFFECT | RENDER_EFFECT | SYNC_EFFECT;
+const IS_EFFECT = EFFECT | PRE_EFFECT | RENDER_EFFECT;
 
 const FLUSH_MICROTASK = 0;
 const FLUSH_SYNC = 1;
@@ -536,7 +540,7 @@ function process_microtask() {
  */
 export function schedule_effect(signal, sync) {
 	const flags = signal.f;
-	if (sync || (flags & SYNC_EFFECT) !== 0) {
+	if (sync) {
 		execute_effect(signal);
 		set_signal_status(signal, CLEAN);
 	} else {
@@ -1284,14 +1288,6 @@ export function invalidate_effect(init) {
 	return internal_create_effect(PRE_EFFECT, init, true, current_block, true);
 }
 
-/**
- * @param {() => void | (() => void)} init
- * @returns {import('./types.js').EffectSignal}
- */
-function sync_effect(init) {
-	return internal_create_effect(SYNC_EFFECT, init, true, current_block, true);
-}
-
 /**
  * @template {import('./types.js').Block} B
  * @param {(block: B) => void | (() => void)} init
@@ -1390,124 +1386,114 @@ export function is_store(val) {
 
 /**
  * This function is responsible for synchronizing a possibly bound prop with the inner component state.
- * It is used whenever the compiler sees that the component writes to the prop.
- *
- * - If the parent passes down a prop without binding, like `<Component prop={value} />`, then create a signal
- *   that updates whenever the value is updated from the parent or from within the component itself
- * - If the parent passes down a prop with a binding, like `<Component bind:prop={value} />`, then
- *   - if the thing that is passed along is the original signal (not a property on it), and the equality functions
- *	 are equal, then just use that signal, no need to create an intermediate one
- *   - otherwise create a signal that updates whenever the value is updated from the parent, and when it's updated
- *	 from within the component itself, call the setter of the parent which will propagate the value change back
+ * It is used whenever the compiler sees that the component writes to the prop, or when it has a default prop_value.
  * @template V
  * @param {Record<string, unknown>} props
  * @param {string} key
  * @param {number} flags
- * @param {V | (() => V)} [default_value]
- * @returns {import('./types.js').Signal<V> | (() => V)}
+ * @param {V | (() => V)} [initial]
+ * @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V))}
  */
-export function prop_source(props, key, flags, default_value) {
-	const call_default_value = (flags & PROPS_CALL_DEFAULT_VALUE) !== 0;
-	const immutable = (flags & PROPS_IS_IMMUTABLE) !== 0;
-	const runes = (flags & PROPS_IS_RUNES) !== 0;
-
-	const update_bound_prop = get_descriptor(props, key)?.set;
-	let value = props[key];
-	const should_set_default_value = value === undefined && default_value !== undefined;
+export function prop(props, key, flags, initial) {
+	var immutable = (flags & PROPS_IS_IMMUTABLE) !== 0;
+	var runes = (flags & PROPS_IS_RUNES) !== 0;
 
-	if (update_bound_prop && runes && default_value !== undefined) {
+	var setter = get_descriptor(props, key)?.set;
+	if (DEV && setter && runes && initial !== undefined) {
 		// TODO consolidate all these random runtime errors
 		throw new Error('Cannot use fallback values with bind:');
 	}
 
-	if (should_set_default_value) {
-		value =
-			// @ts-expect-error would need a cumbersome method overload to type this
-			call_default_value ? default_value() : default_value;
+	var prop_value = /** @type {V} */ (props[key]);
+
+	if (prop_value === undefined && initial !== undefined) {
+		// @ts-expect-error would need a cumbersome method overload to type this
+		if ((flags & PROPS_IS_LAZY_INITIAL) !== 0) initial = initial();
 
 		if (DEV && runes) {
-			value = readonly(proxy(/** @type {any} */ (value)));
+			initial = readonly(proxy(/** @type {any} */ (initial)));
 		}
+
+		prop_value = /** @type {V} */ (initial);
+
+		if (setter) setter(prop_value);
 	}
 
-	const source_signal = immutable ? source(value) : mutable_source(value);
+	var getter = () => {
+		var value = /** @type {V} */ (props[key]);
+		if (value !== undefined) initial = undefined;
+		return value === undefined ? /** @type {V} */ (initial) : value;
+	};
 
-	// Synchronize prop changes with source signal.
-	// Needs special equality checking because the prop in the
-	// parent could be changed through `foo.bar = 'new value'`.
-	let ignore_next1 = false;
-	let ignore_next2 = false;
-	let did_update_to_defined = !should_set_default_value;
+	// easy mode — prop is never written to
+	if ((flags & PROPS_IS_UPDATED) === 0) {
+		return getter;
+	}
 
-	let mount = true;
-	sync_effect(() => {
-		observe(props);
+	// intermediate mode — prop is written to, but the parent component had
+	// `bind:foo` which means we can just call `$$props.foo = value` directly
+	if (setter) {
+		return function (/** @type {V} */ value) {
+			if (arguments.length === 1) {
+				/** @type {Function} */ (setter)(value);
+				return value;
+			} else {
+				return getter();
+			}
+		};
+	}
 
-		// Before if to ensure signal dependency is registered
-		const propagating_value = props[key];
-		if (mount) {
-			mount = false;
-			return;
-		}
-		if (ignore_next1) {
-			ignore_next1 = false;
-			return;
+	// hard mode. this is where it gets ugly — the value in the child should
+	// synchronize with the parent, but it should also be possible to temporarily
+	// set the value to something else locally.
+	var from_child = false;
+	var was_from_child = false;
+
+	// The derived returns the current value. The underlying mutable
+	// source is written to from various places to persist this value.
+	var inner_current_value = mutable_source(prop_value);
+	var current_value = derived(() => {
+		var parent_value = getter();
+		var child_value = get(inner_current_value);
+
+		if (from_child) {
+			from_child = false;
+			was_from_child = true;
+			return child_value;
 		}
 
-		if (
-			// Ensure that updates from undefined to undefined are ignored
-			(did_update_to_defined || propagating_value !== undefined) &&
-			not_equal(immutable, propagating_value, source_signal.v)
-		) {
-			ignore_next2 = true;
-			did_update_to_defined = true;
-			// TODO figure out why we need it this way and the explain in a comment;
-			// some tests fail is we just do set_signal_value(source_signal, propagating_value)
-			untrack(() => set_signal_value(source_signal, propagating_value));
-		}
+		was_from_child = false;
+		return (inner_current_value.v = parent_value);
 	});
 
-	if (update_bound_prop !== undefined) {
-		let ignore_first = !should_set_default_value;
-		sync_effect(() => {
-			// Before if to ensure signal dependency is registered
-			const propagating_value = get(source_signal);
-			if (ignore_first) {
-				ignore_first = false;
-				return;
-			}
-			if (ignore_next2) {
-				ignore_next2 = false;
-				return;
-			}
+	if (!immutable) current_value.e = safe_equal;
 
-			ignore_next1 = true;
-			did_update_to_defined = true;
-			untrack(() => update_bound_prop(propagating_value));
-		});
-	}
+	return function (/** @type {V} */ value, mutation = false) {
+		var current = get(current_value);
 
-	return /** @type {import('./types.js').Signal<V>} */ (source_signal);
-}
+		// legacy nonsense — need to ensure the source is invalidated when necessary
+		if (is_signals_recorded) {
+			// set this so that we don't reset to the parent value if `d`
+			// is invalidated because of `invalidate_inner_signals` (rather
+			// than because the parent or child value changed)
+			from_child = was_from_child;
+			// invoke getters so that signals are picked up by `invalidate_inner_signals`
+			getter();
+			get(inner_current_value);
+		}
 
-/**
- * @param {boolean} immutable
- * @param {unknown} a
- * @param {unknown} b
- * @returns {boolean}
- */
-function not_equal(immutable, a, b) {
-	return immutable ? immutable_not_equal(a, b) : safe_not_equal(a, b);
-}
+		if (arguments.length > 0) {
+			if (mutation || (immutable ? value !== current : safe_not_equal(value, current))) {
+				from_child = true;
+				set(inner_current_value, mutation ? current : value);
+				get(current_value); // force a synchronisation immediately
+			}
 
-/**
- * @param {unknown} a
- * @param {unknown} b
- * @returns {boolean}
- */
-function immutable_not_equal(a, b) {
-	// eslint-disable-next-line eqeqeq
-	return a != a ? b == b : a !== b;
+			return value;
+		}
+
+		return current;
+	};
 }
 
 /**
@@ -1584,82 +1570,67 @@ export function bubble_event($$props, event) {
 
 /**
  * @param {import('./types.js').Signal<number>} signal
+ * @param {1 | -1} [d]
  * @returns {number}
  */
-export function increment(signal) {
+export function update(signal, d = 1) {
 	const value = get(signal);
-	set_signal_value(signal, value + 1);
+	set_signal_value(signal, value + d);
 	return value;
 }
 
 /**
- * @param {import('./types.js').Store<number>} store
- * @param {number} store_value
- * @returns {number}
- */
-export function increment_store(store, store_value) {
-	store.set(store_value + 1);
-	return store_value;
-}
-
-/**
- * @param {import('./types.js').Signal<number>} signal
+ * @param {((value?: number) => number)} fn
+ * @param {1 | -1} [d]
  * @returns {number}
  */
-export function decrement(signal) {
-	const value = get(signal);
-	set_signal_value(signal, value - 1);
+export function update_prop(fn, d = 1) {
+	const value = fn();
+	fn(value + d);
 	return value;
 }
 
 /**
  * @param {import('./types.js').Store<number>} store
  * @param {number} store_value
+ * @param {1 | -1} [d]
  * @returns {number}
  */
-export function decrement_store(store, store_value) {
-	store.set(store_value - 1);
+export function update_store(store, store_value, d = 1) {
+	store.set(store_value + d);
 	return store_value;
 }
 
 /**
  * @param {import('./types.js').Signal<number>} signal
+ * @param {1 | -1} [d]
  * @returns {number}
  */
-export function increment_pre(signal) {
-	const value = get(signal) + 1;
+export function update_pre(signal, d = 1) {
+	const value = get(signal) + d;
 	set_signal_value(signal, value);
 	return value;
 }
 
 /**
- * @param {import('./types.js').Store<number>} store
- * @param {number} store_value
- * @returns {number}
- */
-export function increment_pre_store(store, store_value) {
-	const value = store_value + 1;
-	store.set(value);
-	return value;
-}
-
-/**
- * @param {import('./types.js').Signal<number>} signal
+ * @param {((value?: number) => number)} fn
+ * @param {1 | -1} [d]
  * @returns {number}
  */
-export function decrement_pre(signal) {
-	const value = get(signal) - 1;
-	set_signal_value(signal, value);
+export function update_pre_prop(fn, d = 1) {
+	const value = fn() + d;
+	fn(value);
 	return value;
 }
 
 /**
  * @param {import('./types.js').Store<number>} store
  * @param {number} store_value
+ * @param {1 | -1} [d]
  * @returns {number}
  */
-export function decrement_pre_store(store, store_value) {
-	const value = store_value - 1;
+export function update_pre_store(store, store_value, d = 1) {
+	const value = store_value + d;
 	store.set(value);
 	return value;
 }
diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js
index a48a454923..048105d0f9 100644
--- a/packages/svelte/src/internal/index.js
+++ b/packages/svelte/src/internal/index.js
@@ -7,7 +7,7 @@ export {
 	source,
 	mutable_source,
 	derived,
-	prop_source,
+	prop,
 	user_effect,
 	render_effect,
 	pre_effect,
@@ -17,14 +17,12 @@ export {
 	safe_equal,
 	tick,
 	untrack,
-	increment,
-	increment_store,
-	decrement,
-	decrement_store,
-	increment_pre,
-	increment_pre_store,
-	decrement_pre,
-	decrement_pre_store,
+	update,
+	update_prop,
+	update_store,
+	update_pre,
+	update_pre_prop,
+	update_pre_store,
 	mutate,
 	mutate_store,
 	value_or_fallback,
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-backflow/Child.svelte b/packages/svelte/tests/runtime-legacy/samples/binding-backflow/Child.svelte
index 4e0f779b12..e2865f9662 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-backflow/Child.svelte
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-backflow/Child.svelte
@@ -16,5 +16,5 @@
 	export let updates = [];
 	$: updates = [...updates, value];
 </script>
-  
+
 <div>child: {value?.foo} | updates: {updates.length}</div>
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-backflow/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-backflow/_config.js
index 9e1561f40e..7eefd6a75c 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-backflow/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-backflow/_config.js
@@ -34,7 +34,7 @@ export default test({
 
 		p = parents['reactive_mutate'];
 		assert.deepEqual(p.value, { foo: 'kid' });
-		assert.equal(p.updates.length, 2);
+		assert.equal(p.updates.length, 1);
 
 		p = parents['init_update'];
 		assert.deepEqual(p.value, { foo: 'kid' });
@@ -42,6 +42,6 @@ export default test({
 
 		p = parents['init_mutate'];
 		assert.deepEqual(p.value, { foo: 'kid' });
-		assert.equal(p.updates.length, 2);
+		assert.equal(p.updates.length, 1);
 	}
 });
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-indirect/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-indirect/_config.js
index 196c74950c..b0db992d3b 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-indirect/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-indirect/_config.js
@@ -80,7 +80,7 @@ export default test({
 			select.dispatchEvent(change);
 		});
 
-		assert.equal(component.selected, tasks[1]);
+		assert.deepEqual(component.selected, tasks[1]); // TODO this should be assert.equal, but that crashes the entire test suite in mysterious ways... something to do with proxies not being reused?
 		assert.ok(!input.checked);
 
 		input.checked = true;
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group-outside-each/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group-outside-each/_config.js
index 95846db35f..5be1d9b38d 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group-outside-each/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group-outside-each/_config.js
@@ -67,7 +67,7 @@ export default test({
 		`
 		);
 
-		component.selected = [values[1], values[2]];
+		component.selected = [component.values[1], component.values[2]];
 		assert.equal(inputs[0].checked, false);
 		assert.equal(inputs[1].checked, true);
 		assert.equal(inputs[2].checked, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group/_config.js
index 14625b3bcb..ee508a564e 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-group/_config.js
@@ -67,7 +67,7 @@ export default test({
 		`
 		);
 
-		component.selected = [values[1], values[2]];
+		component.selected = [component.values[1], component.values[2]];
 		assert.equal(inputs[0].checked, false);
 		assert.equal(inputs[1].checked, true);
 		assert.equal(inputs[2].checked, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-1/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-1/_config.js
index fe1a1e8da3..47362f973b 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-1/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-1/_config.js
@@ -290,7 +290,7 @@ export default test({
 		`
 		);
 
-		component.selected_array = [[values[1], values[2]], [values[2]]];
+		component.selected_array = [[component.values[1], component.values[2]], [component.values[2]]];
 
 		assert.equal(inputs[0].checked, false);
 		assert.equal(inputs[1].checked, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-3/_config.js
index b5a0ec69f1..47c73ea56a 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-3/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-group-each-3/_config.js
@@ -286,7 +286,7 @@ export default test({
 		`
 		);
 
-		component.selected_array = [[values[1], values[2]], [values[2]]];
+		component.selected_array = [[component.values[1], component.values[2]], [component.values[2]]];
 
 		assert.equal(inputs[0].checked, false);
 		assert.equal(inputs[1].checked, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-radio-group/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-radio-group/_config.js
index fb65f53ad2..6a34d7cd5b 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-input-radio-group/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-radio-group/_config.js
@@ -71,7 +71,7 @@ export default test({
 		assert.equal(inputs[1].checked, false);
 		assert.equal(inputs[2].checked, false);
 
-		component.selected = values[2];
+		component.selected = component.values[2];
 		assert.equal(inputs[0].checked, false);
 		assert.equal(inputs[1].checked, false);
 		assert.equal(inputs[2].checked, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder-2/_config.js
index 497364f1be..70a17d5cf9 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder-2/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder-2/_config.js
@@ -21,7 +21,7 @@ export default test({
 		assert.equal(options[1].selected, false);
 		assert.equal(options[0].value, '');
 
-		component.foo = items[0];
+		component.foo = component.items[0];
 		assert.equal(options[0].selected, false);
 		assert.equal(options[1].selected, true);
 
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder/_config.js
index 7989d59d0e..955014cf9e 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-null-placeholder/_config.js
@@ -26,7 +26,7 @@ export default test({
 		assert.equal(options[0].value, '');
 		assert.equal(select.checkValidity(), false);
 
-		component.foo = items[0];
+		component.foo = component.items[0];
 
 		assert.equal(options[0].selected, false);
 		assert.equal(options[1].selected, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-component-dirty/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-component-dirty/_config.js
index 44b0027131..ea0236dcb3 100644
--- a/packages/svelte/tests/runtime-legacy/samples/dynamic-component-dirty/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-component-dirty/_config.js
@@ -1,7 +1,7 @@
 import { test } from '../../test';
 
 /** @type {string[]} */
-const calls = [];
+let calls = [];
 
 export default test({
 	get props() {
@@ -9,7 +9,7 @@ export default test({
 	},
 
 	before_test() {
-		calls.length = 0;
+		calls = [];
 	},
 
 	async test({ assert, component, target, window }) {
diff --git a/packages/svelte/tests/runtime-legacy/samples/select-one-way-bind-object/_config.js b/packages/svelte/tests/runtime-legacy/samples/select-one-way-bind-object/_config.js
index 6948e42ab1..fde9c5625b 100644
--- a/packages/svelte/tests/runtime-legacy/samples/select-one-way-bind-object/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/select-one-way-bind-object/_config.js
@@ -1,6 +1,6 @@
 import { test } from '../../test';
 
-const items = [{}, {}];
+const items = [{ id: 'a' }, { id: 'b' }];
 
 export default test({
 	get props() {
@@ -13,7 +13,7 @@ export default test({
 		assert.equal(options[0].selected, true);
 		assert.equal(options[1].selected, false);
 
-		component.foo = items[1];
+		component.foo = component.items[1];
 
 		assert.equal(options[0].selected, false);
 		assert.equal(options[1].selected, true);
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-stale-value/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-stale-value/_config.js
index 4ae1c413b9..275b6711d7 100644
--- a/packages/svelte/tests/runtime-runes/samples/derived-stale-value/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/derived-stale-value/_config.js
@@ -1,12 +1,13 @@
 import { test } from '../../test';
 import { flushSync } from 'svelte';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1] = target.querySelectorAll('button');
 		flushSync(() => {
 			b1.click();
@@ -20,6 +21,6 @@ export default test({
 		flushSync(() => {
 			b1.click();
 		});
-		assert.deepEqual(component.log, [0, 2, 4]);
+		assert.deepEqual(log, [0, 2, 4]);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-stale-value/log.js b/packages/svelte/tests/runtime-runes/samples/derived-stale-value/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-stale-value/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-stale-value/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-stale-value/main.svelte
index 413028647e..91baf81e4a 100644
--- a/packages/svelte/tests/runtime-runes/samples/derived-stale-value/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/derived-stale-value/main.svelte
@@ -1,5 +1,5 @@
 <script>
-  const {log} = $props();
+	import { log } from './log.js';
 
 	let count = $state(0);
 	const derived = $derived(Math.floor(count / 2));
@@ -12,4 +12,4 @@
 
 <button on:click={() => count += 1}>
 	clicks: {count}
-</button>
\ No newline at end of file
+</button>
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js
index ee690418d6..b745e0de3d 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js
@@ -1,12 +1,13 @@
 import { test } from '../../test';
 import { flushSync } from 'svelte';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1] = target.querySelectorAll('button');
 		flushSync(() => {
 			b1.click();
@@ -14,6 +15,6 @@ export default test({
 		flushSync(() => {
 			b1.click();
 		});
-		assert.deepEqual(component.log, ['init 0', 'cleanup 2', 'init 2', 'cleanup 4', 'init 4']);
+		assert.deepEqual(log, ['init 0', 'cleanup 2', 'init 2', 'cleanup 4', 'init 4']);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/log.js b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte
index 7c84aeddff..a67624b760 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	const {log} = $props();
+	import { log } from './log.js';
 
 	let count = $state(0);
 
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-order/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-order/_config.js
index 55a61a5e6a..dbc73d6d50 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-order/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/effect-order/_config.js
@@ -1,12 +1,13 @@
 import { test } from '../../test';
 import { flushSync } from 'svelte';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1] = target.querySelectorAll('button');
 		flushSync(() => {
 			b1.click();
@@ -14,6 +15,6 @@ export default test({
 		flushSync(() => {
 			b1.click();
 		});
-		assert.deepEqual(component.log, ['A', 'B', 'A', 'B', 'A', 'B']);
+		assert.deepEqual(log, ['A', 'B', 'A', 'B', 'A', 'B']);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-order/log.js b/packages/svelte/tests/runtime-runes/samples/effect-order/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-order/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-order/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-order/main.svelte
index 6f19672425..7c0a555f02 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-order/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/effect-order/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	const {log} = $props();
+	import { log } from './log.js';
 
 	let s = $state(0);
 	let d = $derived(s)
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root/_config.js
index b5e2a1a808..1a2afc898f 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-root/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/effect-root/_config.js
@@ -1,12 +1,13 @@
 import { flushSync } from 'svelte';
 import { test } from '../../test';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1, b2, b3] = target.querySelectorAll('button');
 
 		flushSync(() => {
@@ -14,19 +15,19 @@ export default test({
 			b2.click();
 		});
 
-		assert.deepEqual(component.log, [0, 1]);
+		assert.deepEqual(log, [0, 1]);
 
 		flushSync(() => {
 			b3.click();
 		});
 
-		assert.deepEqual(component.log, [0, 1, 'cleanup 1', 'cleanup 2']);
+		assert.deepEqual(log, [0, 1, 'cleanup 1', 'cleanup 2']);
 
 		flushSync(() => {
 			b1.click();
 			b2.click();
 		});
 
-		assert.deepEqual(component.log, [0, 1, 'cleanup 1', 'cleanup 2']);
+		assert.deepEqual(log, [0, 1, 'cleanup 1', 'cleanup 2']);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root/log.js b/packages/svelte/tests/runtime-runes/samples/effect-root/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-root/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte
index d646bea2c4..7a93718659 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	let { log} = $props();
+	import { log } from './log.js';
 
 	let x = $state(0);
 	let y = $state(0);
diff --git a/packages/svelte/tests/runtime-runes/samples/effect/_config.js b/packages/svelte/tests/runtime-runes/samples/effect/_config.js
index 0e543f9311..1e6333bdeb 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/effect/_config.js
@@ -1,16 +1,17 @@
 import { test } from '../../test';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1, b2] = target.querySelectorAll('button');
 		b1.click();
 		b2.click();
 		await Promise.resolve();
 
-		assert.deepEqual(component.log, [0, 1]);
+		assert.deepEqual(log, [0, 1]);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/effect/log.js b/packages/svelte/tests/runtime-runes/samples/effect/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/effect/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect/main.svelte
index 2931aaa15f..6ac6c4b41b 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/effect/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	let { log} = $props();
+	import { log } from './log.js';
 
 	let x = $state(0);
 	let y = $state(0);
diff --git a/packages/svelte/tests/runtime-runes/samples/effects-order/_config.js b/packages/svelte/tests/runtime-runes/samples/effects-order/_config.js
index 7108c005ae..25b05af207 100644
--- a/packages/svelte/tests/runtime-runes/samples/effects-order/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/effects-order/_config.js
@@ -1,16 +1,17 @@
 import { test } from '../../test';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1, b2] = target.querySelectorAll('button');
 		b1.click();
 		b2.click();
 		await Promise.resolve();
 
-		assert.deepEqual(component.log, ['first0', 'second0', 'first1', 'second1']);
+		assert.deepEqual(log, ['first0', 'second0', 'first1', 'second1']);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/effects-order/log.js b/packages/svelte/tests/runtime-runes/samples/effects-order/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effects-order/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/effects-order/main.svelte b/packages/svelte/tests/runtime-runes/samples/effects-order/main.svelte
index 673a579cea..5d8973be35 100644
--- a/packages/svelte/tests/runtime-runes/samples/effects-order/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/effects-order/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	let { log} = $props();
+	import { log } from './log.js';
 
 	let x = $state(0);
 	let y = $state(0);
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/Item.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/Item.svelte
index c458950cba..5375195617 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/Item.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/Item.svelte
@@ -1,21 +1,23 @@
 <script>
-	let { index, n, order } = $props();
+	import { log } from './log.js';
+
+	let { index, n } = $props();
 
 	function logRender () {
-		order.push(`${index}: render ${n}`);
+		log.push(`${index}: render ${n}`);
 		return index;
 	}
 
 	$effect.pre(() => {
-		order.push(`${index}: $effect.pre ${n}`);
+		log.push(`${index}: $effect.pre ${n}`);
 	});
 
 	$effect.pre(() => {
-		order.push(`${index}: $effect.pre (2) ${n}`);
+		log.push(`${index}: $effect.pre (2) ${n}`);
 	});
 
 	$effect(() => {
-		order.push(`${index}: $effect ${n}`);
+		log.push(`${index}: $effect ${n}`);
 	});
 </script>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js
index 098a6c8c58..2667fff13c 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js
@@ -1,24 +1,17 @@
 import { test } from '../../test';
-
-/**
- * @type {any[]}
- */
-let order = [];
-let n = 0;
+import { log } from './log.js';
 
 export default test({
 	get props() {
-		return {
-			order,
-			n
-		};
+		return { n: 0 };
 	},
+
 	before_test() {
-		order.length = 0;
-		n = 0;
+		log.length = 0;
 	},
-	async test({ assert, compileOptions, component }) {
-		assert.deepEqual(order, [
+
+	async test({ assert, component }) {
+		assert.deepEqual(log, [
 			'parent: $effect.pre 0',
 			'parent: $effect.pre (2) 0',
 			'parent: render 0',
@@ -37,11 +30,11 @@ export default test({
 			'parent: $effect 0'
 		]);
 
-		order.length = 0;
+		log.length = 0;
 
 		component.n += 1;
 
-		assert.deepEqual(order, [
+		assert.deepEqual(log, [
 			'parent: $effect.pre 1',
 			'parent: $effect.pre (2) 1',
 			'parent: render 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/log.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/main.svelte
index bab1b4cd27..cc21df4f1b 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/main.svelte
@@ -1,30 +1,31 @@
 <script>
 	import Item from './Item.svelte';
+	import { log } from './log.js';
 
-	let { n = 0, order } = $props();
+	let { n = 0 } = $props();
 
 	function logRender () {
-		order.push(`parent: render ${n}`);
+		log.push(`parent: render ${n}`);
 		return 'parent';
 	}
 
 	$effect.pre(() => {
-		order.push(`parent: $effect.pre ${n}`);
+		log.push(`parent: $effect.pre ${n}`);
 	});
 
 	$effect.pre(() => {
-		order.push(`parent: $effect.pre (2) ${n}`);
+		log.push(`parent: $effect.pre (2) ${n}`);
 	});
 
 	$effect(() => {
-		order.push(`parent: $effect ${n}`);
+		log.push(`parent: $effect ${n}`);
 	});
 </script>
 
 {logRender()}
 <ul>
 	{#each [1,2,3] as index}
-		<Item {index} {n} {order} />
+		<Item {index} {n} />
 	{/each}
 </ul>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/Item.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/Item.svelte
index eb9bf54f12..8719c578f9 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/Item.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/Item.svelte
@@ -1,17 +1,19 @@
 <script>
-	let { index, n, order } = $props();
+	import { log } from './log.js';
+
+	let { index, n } = $props();
 
 	function logRender () {
-		order.push(`${index}: render ${n}`);
+		log.push(`${index}: render ${n}`);
 		return index;
 	}
 
 	$effect.pre(() => {
-		order.push(`${index}: $effect.pre ${n}`);
+		log.push(`${index}: $effect.pre ${n}`);
 	});
 
 	$effect(() => {
-		order.push(`${index}: $effect ${n}`);
+		log.push(`${index}: $effect ${n}`);
 	});
 </script>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js
index 6547999c0c..e528189597 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js
@@ -1,24 +1,17 @@
 import { test } from '../../test';
-
-/**
- * @type {any[]}
- */
-let order = [];
-let n = 0;
+import { log } from './log.js';
 
 export default test({
 	get props() {
-		return {
-			order,
-			n
-		};
+		return { n: 0 };
 	},
+
 	before_test() {
-		order.length = 0;
-		n = 0;
+		log.length = 0;
 	},
-	async test({ assert, compileOptions, component }) {
-		assert.deepEqual(order, [
+
+	async test({ assert, component }) {
+		assert.deepEqual(log, [
 			'parent: render 0',
 			'1: $effect.pre 0',
 			'1: render 0',
@@ -32,11 +25,11 @@ export default test({
 			'parent: $effect 0'
 		]);
 
-		order.length = 0;
+		log.length = 0;
 
 		component.n += 1;
 
-		assert.deepEqual(order, [
+		assert.deepEqual(log, [
 			'parent: render 1',
 			'1: $effect.pre 1',
 			'1: render 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/log.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/main.svelte
index 6a84700f0d..57d85095d5 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/main.svelte
@@ -1,22 +1,23 @@
 <script>
 	import Item from './Item.svelte';
+	import { log } from './log.js';
 
-	let { n = 0, order } = $props();
+	let { n = 0 } = $props();
 
 	function logRender () {
-		order.push(`parent: render ${n}`);
+		log.push(`parent: render ${n}`);
 		return 'parent';
 	}
 
 	$effect(() => {
-		order.push(`parent: $effect ${n}`);
+		log.push(`parent: $effect ${n}`);
 	});
 </script>
 
 {logRender()}
 <ul>
 	{#each [1,2,3] as index}
-		<Item {index} {n} {order} />
+		<Item {index} {n} />
 	{/each}
 </ul>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/Item.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/Item.svelte
index 4e048313a2..2e9dc09c7a 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/Item.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/Item.svelte
@@ -1,21 +1,23 @@
 <script>
-	let { index, n, order } = $props();
+	import { log } from './log.js';
+
+	let { index, n } = $props();
 
 	function logRender () {
-		order.push(`${index}: render ${n}`);
+		log.push(`${index}: render ${n}`);
 		return index;
 	}
 
 	$effect.pre(() => {
-		order.push(`${index}: $effect.pre ${n}`);
+		log.push(`${index}: $effect.pre ${n}`);
 
 		$effect.pre(() => {
-			order.push(`${index}: nested $effect.pre ${n}`);
+			log.push(`${index}: nested $effect.pre ${n}`);
 		});
 	});
 
 	$effect(() => {
-		order.push(`${index}: $effect ${n}`);
+		log.push(`${index}: $effect ${n}`);
 	});
 </script>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js
index e61b574d27..62a4b85546 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js
@@ -1,24 +1,17 @@
 import { test } from '../../test';
-
-/**
- * @type {any[]}
- */
-let order = [];
-let n = 0;
+import { log } from './log.js';
 
 export default test({
 	get props() {
-		return {
-			order,
-			n
-		};
+		return { n: 0 };
 	},
+
 	before_test() {
-		order.length = 0;
-		n = 0;
+		log.length = 0;
 	},
-	async test({ assert, compileOptions, component }) {
-		assert.deepEqual(order, [
+
+	async test({ assert, component }) {
+		assert.deepEqual(log, [
 			'parent: $effect.pre 0',
 			'parent: nested $effect.pre 0',
 			'parent: render 0',
@@ -37,11 +30,11 @@ export default test({
 			'parent: $effect 0'
 		]);
 
-		order.length = 0;
+		log.length = 0;
 
 		component.n += 1;
 
-		assert.deepEqual(order, [
+		assert.deepEqual(log, [
 			'parent: $effect.pre 1',
 			'parent: nested $effect.pre 1',
 			'parent: render 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/log.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/main.svelte
index 644bef3278..d9b83f837c 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/main.svelte
@@ -1,30 +1,31 @@
 <script>
 	import Item from './Item.svelte';
+	import { log } from './log.js';
 
-	let { n = 0, order } = $props();
+	let { n = 0 } = $props();
 
 	function logRender () {
-		order.push(`parent: render ${n}`);
+		log.push(`parent: render ${n}`);
 		return 'parent';
 	}
 
 	$effect.pre(() => {
-		order.push(`parent: $effect.pre ${n}`);
+		log.push(`parent: $effect.pre ${n}`);
 
 		$effect.pre(() => {
-			order.push(`parent: nested $effect.pre ${n}`);
+			log.push(`parent: nested $effect.pre ${n}`);
 		});
 	});
 
 	$effect(() => {
-		order.push(`parent: $effect ${n}`);
+		log.push(`parent: $effect ${n}`);
 	});
 </script>
 
 {logRender()}
 <ul>
 	{#each [1,2,3] as index}
-		<Item {index} {n} {order} />
+		<Item {index} {n} />
 	{/each}
 </ul>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/Item.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/Item.svelte
index eb9bf54f12..8719c578f9 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/Item.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/Item.svelte
@@ -1,17 +1,19 @@
 <script>
-	let { index, n, order } = $props();
+	import { log } from './log.js';
+
+	let { index, n } = $props();
 
 	function logRender () {
-		order.push(`${index}: render ${n}`);
+		log.push(`${index}: render ${n}`);
 		return index;
 	}
 
 	$effect.pre(() => {
-		order.push(`${index}: $effect.pre ${n}`);
+		log.push(`${index}: $effect.pre ${n}`);
 	});
 
 	$effect(() => {
-		order.push(`${index}: $effect ${n}`);
+		log.push(`${index}: $effect ${n}`);
 	});
 </script>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js
index f9b5122151..0c7118df75 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js
@@ -1,24 +1,17 @@
 import { test } from '../../test';
-
-/**
- * @type {any[]}
- */
-let order = [];
-let n = 0;
+import { log } from './log.js';
 
 export default test({
 	get props() {
-		return {
-			order,
-			n
-		};
+		return { n: 0 };
 	},
+
 	before_test() {
-		order.length = 0;
-		n = 0;
+		log.length = 0;
 	},
-	async test({ assert, compileOptions, component }) {
-		assert.deepEqual(order, [
+
+	async test({ assert, component }) {
+		assert.deepEqual(log, [
 			'parent: $effect.pre 0',
 			'parent: render 0',
 			'1: $effect.pre 0',
@@ -33,11 +26,11 @@ export default test({
 			'parent: $effect 0'
 		]);
 
-		order.length = 0;
+		log.length = 0;
 
 		component.n += 1;
 
-		assert.deepEqual(order, [
+		assert.deepEqual(log, [
 			'parent: $effect.pre 1',
 			'parent: render 1',
 			'1: $effect.pre 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/log.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/main.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/main.svelte
index fd2cce5beb..9cfa2b834c 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/main.svelte
@@ -1,26 +1,27 @@
 <script>
 	import Item from './Item.svelte';
+	import { log } from './log.js';
 
-	let { n = 0, order } = $props();
+	let { n = 0 } = $props();
 
 	function logRender () {
-		order.push(`parent: render ${n}`);
+		log.push(`parent: render ${n}`);
 		return 'parent';
 	}
 
 	$effect.pre(() => {
-		order.push(`parent: $effect.pre ${n}`);
+		log.push(`parent: $effect.pre ${n}`);
 	});
 
 	$effect(() => {
-		order.push(`parent: $effect ${n}`);
+		log.push(`parent: $effect ${n}`);
 	});
 </script>
 
 {logRender()}
 <ul>
 	{#each [1,2,3] as index}
-		<Item {index} {n} {order} />
+		<Item {index} {n} />
 	{/each}
 </ul>
 
diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-operator/_config.js b/packages/svelte/tests/runtime-runes/samples/nullish-operator/_config.js
index 4c425bc5f7..11e729c3c7 100644
--- a/packages/svelte/tests/runtime-runes/samples/nullish-operator/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/nullish-operator/_config.js
@@ -1,13 +1,14 @@
 import { test } from '../../test';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert }) {
 		await Promise.resolve();
 		await Promise.resolve();
-		assert.deepEqual(component.log, ['a1: ', true, 'b1: ', true]);
+		assert.deepEqual(log, ['a1: ', true, 'b1: ', true]);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-operator/log.js b/packages/svelte/tests/runtime-runes/samples/nullish-operator/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/nullish-operator/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-operator/main.svelte b/packages/svelte/tests/runtime-runes/samples/nullish-operator/main.svelte
index 892973063a..a98c4b4f39 100644
--- a/packages/svelte/tests/runtime-runes/samples/nullish-operator/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/nullish-operator/main.svelte
@@ -1,12 +1,13 @@
 <script>
+	import { log } from './log.js';
+
 	let a1 = $state();
 	let b1 = $state();
 
-	const {log} = $props();
-
 	$effect(() => {
 		log.push('a1: ', a1);
 	});
+
 	$effect(() => {
 		log.push('b1: ', b1);
 	});
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/_config.js b/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/_config.js
index 8fe474169d..00463c2e18 100644
--- a/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/_config.js
@@ -1,12 +1,13 @@
 import { flushSync } from 'svelte';
 import { test } from '../../test';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1] = target.querySelectorAll('button');
 
 		flushSync(() => {
@@ -16,7 +17,7 @@ export default test({
 			b1.click();
 		});
 
-		assert.deepEqual(component.log, [
+		assert.deepEqual(log, [
 			'Outer Effect Start (0)',
 			'Outer Effect End (0)',
 			'Inner Effect (0)',
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/log.js b/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/main.svelte b/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/main.svelte
index e31882fbbd..19dff4097d 100644
--- a/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/pre-effect-ordering/main.svelte
@@ -1,11 +1,12 @@
 <script>
-    const {log} = $props();
+    import { log } from './log.js';
+
 	let count = $state(0);
 
 	function increment() {
 		count += 1;
 	}
-	
+
 	$effect.pre(() => {
 		log.push(`Outer Effect Start (${count})`)
 
@@ -19,4 +20,4 @@
 
 <button on:click={increment}>
 	Count: {count}
-</button>
\ No newline at end of file
+</button>
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-effect/_config.js b/packages/svelte/tests/runtime-runes/samples/pre-effect/_config.js
index 0e543f9311..1e6333bdeb 100644
--- a/packages/svelte/tests/runtime-runes/samples/pre-effect/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/pre-effect/_config.js
@@ -1,16 +1,17 @@
 import { test } from '../../test';
+import { log } from './log.js';
 
 export default test({
-	get props() {
-		return { log: [] };
+	before_test() {
+		log.length = 0;
 	},
 
-	async test({ assert, target, component }) {
+	async test({ assert, target }) {
 		const [b1, b2] = target.querySelectorAll('button');
 		b1.click();
 		b2.click();
 		await Promise.resolve();
 
-		assert.deepEqual(component.log, [0, 1]);
+		assert.deepEqual(log, [0, 1]);
 	}
 });
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-effect/log.js b/packages/svelte/tests/runtime-runes/samples/pre-effect/log.js
new file mode 100644
index 0000000000..d3df521f4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/pre-effect/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-effect/main.svelte b/packages/svelte/tests/runtime-runes/samples/pre-effect/main.svelte
index cc9a249691..5245340373 100644
--- a/packages/svelte/tests/runtime-runes/samples/pre-effect/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/pre-effect/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	let { log} = $props();
+	import { log } from './log.js';
 
 	let x = $state(0);
 	let y = $state(0);
diff --git a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js
index bd4c1a30a1..ac61fc9e6d 100644
--- a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js
@@ -6,12 +6,12 @@ import * as $ from "svelte/internal";
 export default function Svelte_element($$anchor, $$props) {
 	$.push($$props, true);
 
-	let tag = $.prop_source($$props, "tag", 3, 'hr');
+	let tag = $.prop($$props, "tag", 3, 'hr');
 	/* Init */
 	var fragment = $.comment($$anchor);
 	var node = $.child_frag(fragment);
 
-	$.element(node, () => $.get(tag));
+	$.element(node, () => tag());
 	$.close_frag($$anchor, fragment);
 	$.pop();
-}
\ No newline at end of file
+}