|
|
@ -8,12 +8,11 @@ import {
|
|
|
|
PROPS_IS_UPDATED
|
|
|
|
PROPS_IS_UPDATED
|
|
|
|
} from '../../../constants.js';
|
|
|
|
} from '../../../constants.js';
|
|
|
|
import { get_descriptor, is_function } from '../../shared/utils.js';
|
|
|
|
import { get_descriptor, is_function } from '../../shared/utils.js';
|
|
|
|
import { mutable_source, set, source, update } from './sources.js';
|
|
|
|
import { set, source, update } from './sources.js';
|
|
|
|
import { derived, derived_safe_equal } from './deriveds.js';
|
|
|
|
import { derived, derived_safe_equal } from './deriveds.js';
|
|
|
|
import { get, captured_signals, untrack } from '../runtime.js';
|
|
|
|
import { get, untrack } from '../runtime.js';
|
|
|
|
import { safe_equals } from './equality.js';
|
|
|
|
|
|
|
|
import * as e from '../errors.js';
|
|
|
|
import * as e from '../errors.js';
|
|
|
|
import { LEGACY_DERIVED_PROP, LEGACY_PROPS, STATE_SYMBOL } from '#client/constants';
|
|
|
|
import { LEGACY_PROPS, STATE_SYMBOL } from '#client/constants';
|
|
|
|
import { proxy } from '../proxy.js';
|
|
|
|
import { proxy } from '../proxy.js';
|
|
|
|
import { capture_store_binding } from './store.js';
|
|
|
|
import { capture_store_binding } from './store.js';
|
|
|
|
import { legacy_mode_flag } from '../../flags/index.js';
|
|
|
|
import { legacy_mode_flag } from '../../flags/index.js';
|
|
|
@ -260,89 +259,92 @@ function has_destroyed_component_ctx(current_value) {
|
|
|
|
* @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V))}
|
|
|
|
* @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V))}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
export function prop(props, key, flags, fallback) {
|
|
|
|
export function prop(props, key, flags, fallback) {
|
|
|
|
var immutable = (flags & PROPS_IS_IMMUTABLE) !== 0;
|
|
|
|
|
|
|
|
var runes = !legacy_mode_flag || (flags & PROPS_IS_RUNES) !== 0;
|
|
|
|
var runes = !legacy_mode_flag || (flags & PROPS_IS_RUNES) !== 0;
|
|
|
|
var bindable = (flags & PROPS_IS_BINDABLE) !== 0;
|
|
|
|
var bindable = (flags & PROPS_IS_BINDABLE) !== 0;
|
|
|
|
var lazy = (flags & PROPS_IS_LAZY_INITIAL) !== 0;
|
|
|
|
var lazy = (flags & PROPS_IS_LAZY_INITIAL) !== 0;
|
|
|
|
var is_store_sub = false;
|
|
|
|
|
|
|
|
var prop_value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bindable) {
|
|
|
|
|
|
|
|
[prop_value, is_store_sub] = capture_store_binding(() => /** @type {V} */ (props[key]));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
prop_value = /** @type {V} */ (props[key]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Can be the case when someone does `mount(Component, props)` with `let props = $state({...})`
|
|
|
|
|
|
|
|
// or `createClassComponent(Component, props)`
|
|
|
|
|
|
|
|
var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var setter =
|
|
|
|
|
|
|
|
(bindable &&
|
|
|
|
|
|
|
|
(get_descriptor(props, key)?.set ??
|
|
|
|
|
|
|
|
(is_entry_props && key in props && ((v) => (props[key] = v))))) ||
|
|
|
|
|
|
|
|
undefined;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var fallback_value = /** @type {V} */ (fallback);
|
|
|
|
var fallback_value = /** @type {V} */ (fallback);
|
|
|
|
var fallback_dirty = true;
|
|
|
|
var fallback_dirty = true;
|
|
|
|
var fallback_used = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var get_fallback = () => {
|
|
|
|
var get_fallback = () => {
|
|
|
|
fallback_used = true;
|
|
|
|
|
|
|
|
if (fallback_dirty) {
|
|
|
|
if (fallback_dirty) {
|
|
|
|
fallback_dirty = false;
|
|
|
|
fallback_dirty = false;
|
|
|
|
if (lazy) {
|
|
|
|
|
|
|
|
fallback_value = untrack(/** @type {() => V} */ (fallback));
|
|
|
|
fallback_value = lazy
|
|
|
|
} else {
|
|
|
|
? untrack(/** @type {() => V} */ (fallback))
|
|
|
|
fallback_value = /** @type {V} */ (fallback);
|
|
|
|
: /** @type {V} */ (fallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return fallback_value;
|
|
|
|
return fallback_value;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (prop_value === undefined && fallback !== undefined) {
|
|
|
|
/** @type {((v: V) => void) | undefined} */
|
|
|
|
if (setter && runes) {
|
|
|
|
var setter;
|
|
|
|
e.props_invalid_value(key);
|
|
|
|
|
|
|
|
|
|
|
|
if (bindable) {
|
|
|
|
|
|
|
|
// Can be the case when someone does `mount(Component, props)` with `let props = $state({...})`
|
|
|
|
|
|
|
|
// or `createClassComponent(Component, props)`
|
|
|
|
|
|
|
|
var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setter =
|
|
|
|
|
|
|
|
get_descriptor(props, key)?.set ??
|
|
|
|
|
|
|
|
(is_entry_props && key in props ? (v) => (props[key] = v) : undefined);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var initial_value;
|
|
|
|
|
|
|
|
var is_store_sub = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bindable) {
|
|
|
|
|
|
|
|
[initial_value, is_store_sub] = capture_store_binding(() => /** @type {V} */ (props[key]));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
initial_value = /** @type {V} */ (props[key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
prop_value = get_fallback();
|
|
|
|
if (initial_value === undefined && fallback !== undefined) {
|
|
|
|
if (setter) setter(prop_value);
|
|
|
|
initial_value = get_fallback();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (setter) {
|
|
|
|
|
|
|
|
if (runes) e.props_invalid_value(key);
|
|
|
|
|
|
|
|
setter(initial_value);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {() => V} */
|
|
|
|
/** @type {() => V} */
|
|
|
|
var getter;
|
|
|
|
var getter;
|
|
|
|
|
|
|
|
|
|
|
|
if (runes) {
|
|
|
|
if (runes) {
|
|
|
|
getter = () => {
|
|
|
|
getter = () => {
|
|
|
|
var value = /** @type {V} */ (props[key]);
|
|
|
|
var value = /** @type {V} */ (props[key]);
|
|
|
|
if (value === undefined) return get_fallback();
|
|
|
|
if (value === undefined) return get_fallback();
|
|
|
|
fallback_dirty = true;
|
|
|
|
fallback_dirty = true;
|
|
|
|
fallback_used = false;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Svelte 4 did not trigger updates when a primitive value was updated to the same value.
|
|
|
|
|
|
|
|
// Replicate that behavior through using a derived
|
|
|
|
|
|
|
|
var derived_getter = (immutable ? derived : derived_safe_equal)(
|
|
|
|
|
|
|
|
() => /** @type {V} */ (props[key])
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
derived_getter.f |= LEGACY_DERIVED_PROP;
|
|
|
|
|
|
|
|
getter = () => {
|
|
|
|
getter = () => {
|
|
|
|
var value = get(derived_getter);
|
|
|
|
var value = /** @type {V} */ (props[key]);
|
|
|
|
if (value !== undefined) fallback_value = /** @type {V} */ (undefined);
|
|
|
|
|
|
|
|
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
|
|
|
// in legacy mode, we don't revert to the fallback value
|
|
|
|
|
|
|
|
// if the prop goes from defined to undefined. The easiest
|
|
|
|
|
|
|
|
// way to model this is to make the fallback undefined
|
|
|
|
|
|
|
|
// as soon as the prop has a value
|
|
|
|
|
|
|
|
fallback_value = /** @type {V} */ (undefined);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return value === undefined ? fallback_value : value;
|
|
|
|
return value === undefined ? fallback_value : value;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// easy mode — prop is never written to
|
|
|
|
// prop is never written to — we only need a getter
|
|
|
|
if ((flags & PROPS_IS_UPDATED) === 0 && runes) {
|
|
|
|
if (runes && (flags & PROPS_IS_UPDATED) === 0) {
|
|
|
|
return getter;
|
|
|
|
return getter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// intermediate mode — prop is written to, but the parent component had
|
|
|
|
// prop is written to, but the parent component had `bind:foo` which
|
|
|
|
// `bind:foo` which means we can just call `$$props.foo = value` directly
|
|
|
|
// means we can just call `$$props.foo = value` directly
|
|
|
|
if (setter) {
|
|
|
|
if (setter) {
|
|
|
|
var legacy_parent = props.$$legacy;
|
|
|
|
var legacy_parent = props.$$legacy;
|
|
|
|
|
|
|
|
|
|
|
|
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
|
|
|
|
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
|
|
|
|
if (arguments.length > 0) {
|
|
|
|
if (arguments.length > 0) {
|
|
|
|
// We don't want to notify if the value was mutated and the parent is in runes mode.
|
|
|
|
// We don't want to notify if the value was mutated and the parent is in runes mode.
|
|
|
@ -352,82 +354,41 @@ export function prop(props, key, flags, fallback) {
|
|
|
|
if (!runes || !mutation || legacy_parent || is_store_sub) {
|
|
|
|
if (!runes || !mutation || legacy_parent || is_store_sub) {
|
|
|
|
/** @type {Function} */ (setter)(mutation ? getter() : value);
|
|
|
|
/** @type {Function} */ (setter)(mutation ? getter() : value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
return value;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return getter();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
return getter();
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
was_from_child = false;
|
|
|
|
// Either prop is written to, but there's no binding, which means we
|
|
|
|
return (inner_current_value.v = parent_value);
|
|
|
|
// create a derived that we can write to locally.
|
|
|
|
});
|
|
|
|
// Or we are in legacy mode where we always create a derived to replicate that
|
|
|
|
|
|
|
|
// Svelte 4 did not trigger updates when a primitive value was updated to the same value.
|
|
|
|
// Ensure we eagerly capture the initial value if it's bindable
|
|
|
|
var d = ((flags & PROPS_IS_IMMUTABLE) !== 0 ? derived : derived_safe_equal)(getter);
|
|
|
|
if (bindable) {
|
|
|
|
|
|
|
|
get(current_value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!immutable) current_value.equals = safe_equals;
|
|
|
|
// Capture the initial value if it's bindable
|
|
|
|
|
|
|
|
if (bindable) get(d);
|
|
|
|
|
|
|
|
|
|
|
|
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
|
|
|
|
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
|
|
|
|
// legacy nonsense — need to ensure the source is invalidated when necessary
|
|
|
|
|
|
|
|
// also needed for when handling inspect logic so we can inspect the correct source signal
|
|
|
|
|
|
|
|
if (captured_signals !== null) {
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (arguments.length > 0) {
|
|
|
|
if (arguments.length > 0) {
|
|
|
|
const new_value = mutation ? get(current_value) : runes && bindable ? proxy(value) : value;
|
|
|
|
const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value;
|
|
|
|
|
|
|
|
|
|
|
|
if (!current_value.equals(new_value)) {
|
|
|
|
|
|
|
|
from_child = true;
|
|
|
|
|
|
|
|
set(inner_current_value, new_value);
|
|
|
|
|
|
|
|
// To ensure the fallback value is consistent when used with proxies, we
|
|
|
|
|
|
|
|
// update the local fallback_value, but only if the fallback is actively used
|
|
|
|
|
|
|
|
if (fallback_used && fallback_value !== undefined) {
|
|
|
|
|
|
|
|
fallback_value = new_value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (has_destroyed_component_ctx(current_value)) {
|
|
|
|
set(d, new_value);
|
|
|
|
return value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
untrack(() => get(current_value)); // force a synchronisation immediately
|
|
|
|
if (fallback_value !== undefined) {
|
|
|
|
|
|
|
|
fallback_value = new_value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (has_destroyed_component_ctx(current_value)) {
|
|
|
|
// TODO is this still necessary post-#16263?
|
|
|
|
return current_value.v;
|
|
|
|
if (has_destroyed_component_ctx(d)) {
|
|
|
|
|
|
|
|
return d.v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return get(current_value);
|
|
|
|
return get(d);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|