pull/16278/head
Rich Harris 3 months ago
parent 05636d309b
commit c8ab9e6104

@ -1,5 +1,5 @@
/** @import { ComponentContext } from '#client' */ /** @import { ComponentContext } from '#client' */
/** @import { Derived, Source } from './types.js' */ /** @import { Derived, Effect, Source } from './types.js' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { import {
PROPS_IS_BINDABLE, PROPS_IS_BINDABLE,
@ -12,9 +12,15 @@ import {
import { get_descriptor, is_function } from '../../shared/utils.js'; import { get_descriptor, is_function } from '../../shared/utils.js';
import { 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, is_destroying_effect, untrack } from '../runtime.js'; import {
active_effect,
get,
is_destroying_effect,
set_active_effect,
untrack
} from '../runtime.js';
import * as e from '../errors.js'; import * as e from '../errors.js';
import { LEGACY_PROPS, STATE_SYMBOL } from '#client/constants'; import { DESTROYED, 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';
@ -94,7 +100,7 @@ export function rest_props(props, exclude, name) {
/** /**
* The proxy handler for legacy $$restProps and $$props * The proxy handler for legacy $$restProps and $$props
* @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number> }>}} * @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number>, parent_effect: Effect }>}}
*/ */
const legacy_rest_props_handler = { const legacy_rest_props_handler = {
get(target, key) { get(target, key) {
@ -104,17 +110,25 @@ const legacy_rest_props_handler = {
}, },
set(target, key, value) { set(target, key, value) {
if (!(key in target.special)) { if (!(key in target.special)) {
// Handle props that can temporarily get out of sync with the parent var previous_effect = active_effect;
/** @type {Record<string, (v?: unknown) => unknown>} */
target.special[key] = prop( try {
{ set_active_effect(target.parent_effect);
get [key]() {
return target.props[key]; // Handle props that can temporarily get out of sync with the parent
} /** @type {Record<string, (v?: unknown) => unknown>} */
}, target.special[key] = prop(
/** @type {string} */ (key), {
PROPS_IS_UPDATED get [key]() {
); return target.props[key];
}
},
/** @type {string} */ (key),
PROPS_IS_UPDATED
);
} finally {
set_active_effect(previous_effect);
}
} }
target.special[key](value); target.special[key](value);
@ -153,7 +167,19 @@ const legacy_rest_props_handler = {
* @returns {Record<string, unknown>} * @returns {Record<string, unknown>}
*/ */
export function legacy_rest_props(props, exclude) { export function legacy_rest_props(props, exclude) {
return new Proxy({ props, exclude, special: {}, version: source(0) }, legacy_rest_props_handler); return new Proxy(
{
props,
exclude,
special: {},
version: source(0),
// TODO this is only necessary because we need to track component
// destruction inside `prop`, because of `bind:this`, but it
// seems likely that we can simplify `bind:this` instead
parent_effect: /** @type {Effect} */ (active_effect)
},
legacy_rest_props_handler
);
} }
/** /**
@ -376,6 +402,12 @@ export function prop(props, key, flags, fallback) {
// Capture the initial value if it's bindable // Capture the initial value if it's bindable
if (bindable) get(d); if (bindable) get(d);
var parent_effect = /** @type {Effect} */ (active_effect);
if (!parent_effect) {
console.trace();
}
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) { return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
if (arguments.length > 0) { if (arguments.length > 0) {
const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value; const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value;
@ -390,10 +422,12 @@ export function prop(props, key, flags, fallback) {
return value; return value;
} }
// special case — avoid recalculating the derived if // special case — avoid recalculating the derived if we're in a
// we're in a teardown function and the prop // teardown function and the prop was overridden locally, or the
// was overridden locally // component was already destroyed (this latter part is necessary
if (is_destroying_effect && overridden) { // because `bind:this` can read props after the component has
// been destroyed. TODO simplify `bind:this`
if ((is_destroying_effect && overridden) || (parent_effect.f & DESTROYED) !== 0) {
return d.v; return d.v;
} }

Loading…
Cancel
Save