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 +}