11/** @import { ComponentContext } from '#client' */
2- /** @import { Derived, Source } from './types.js' */
2+ /** @import { Derived, Effect, Source } from './types.js' */
33import { DEV } from 'esm-env' ;
44import {
55 PROPS_IS_BINDABLE ,
@@ -12,9 +12,15 @@ import {
1212import { get_descriptor , is_function } from '../../shared/utils.js' ;
1313import { set , source , update } from './sources.js' ;
1414import { derived , derived_safe_equal } from './deriveds.js' ;
15- import { get , is_destroying_effect , untrack } from '../runtime.js' ;
15+ import {
16+ active_effect ,
17+ get ,
18+ is_destroying_effect ,
19+ set_active_effect ,
20+ untrack
21+ } from '../runtime.js' ;
1622import * as e from '../errors.js' ;
17- import { LEGACY_PROPS , STATE_SYMBOL } from '#client/constants' ;
23+ import { DESTROYED , LEGACY_PROPS , STATE_SYMBOL } from '#client/constants' ;
1824import { proxy } from '../proxy.js' ;
1925import { capture_store_binding } from './store.js' ;
2026import { legacy_mode_flag } from '../../flags/index.js' ;
@@ -94,7 +100,7 @@ export function rest_props(props, exclude, name) {
94100
95101/**
96102 * The proxy handler for legacy $$restProps and $$props
97- * @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number> }> }}
103+ * @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number>, parent_effect: Effect }> }}
98104 */
99105const legacy_rest_props_handler = {
100106 get ( target , key ) {
@@ -104,17 +110,25 @@ const legacy_rest_props_handler = {
104110 } ,
105111 set ( target , key , value ) {
106112 if ( ! ( key in target . special ) ) {
107- // Handle props that can temporarily get out of sync with the parent
108- /** @type {Record<string, (v?: unknown) => unknown> } */
109- target . special [ key ] = prop (
110- {
111- get [ key ] ( ) {
112- return target . props [ key ] ;
113- }
114- } ,
115- /** @type {string } */ ( key ) ,
116- PROPS_IS_UPDATED
117- ) ;
113+ var previous_effect = active_effect ;
114+
115+ try {
116+ set_active_effect ( target . parent_effect ) ;
117+
118+ // Handle props that can temporarily get out of sync with the parent
119+ /** @type {Record<string, (v?: unknown) => unknown> } */
120+ target . special [ key ] = prop (
121+ {
122+ get [ key ] ( ) {
123+ return target . props [ key ] ;
124+ }
125+ } ,
126+ /** @type {string } */ ( key ) ,
127+ PROPS_IS_UPDATED
128+ ) ;
129+ } finally {
130+ set_active_effect ( previous_effect ) ;
131+ }
118132 }
119133
120134 target . special [ key ] ( value ) ;
@@ -153,7 +167,19 @@ const legacy_rest_props_handler = {
153167 * @returns {Record<string, unknown> }
154168 */
155169export function legacy_rest_props ( props , exclude ) {
156- return new Proxy ( { props, exclude, special : { } , version : source ( 0 ) } , legacy_rest_props_handler ) ;
170+ return new Proxy (
171+ {
172+ props,
173+ exclude,
174+ special : { } ,
175+ version : source ( 0 ) ,
176+ // TODO this is only necessary because we need to track component
177+ // destruction inside `prop`, because of `bind:this`, but it
178+ // seems likely that we can simplify `bind:this` instead
179+ parent_effect : /** @type {Effect } */ ( active_effect )
180+ } ,
181+ legacy_rest_props_handler
182+ ) ;
157183}
158184
159185/**
@@ -376,6 +402,12 @@ export function prop(props, key, flags, fallback) {
376402 // Capture the initial value if it's bindable
377403 if ( bindable ) get ( d ) ;
378404
405+ var parent_effect = /** @type {Effect } */ ( active_effect ) ;
406+
407+ if ( ! parent_effect ) {
408+ console . trace ( ) ;
409+ }
410+
379411 return function ( /** @type {any } */ value , /** @type {boolean } */ mutation ) {
380412 if ( arguments . length > 0 ) {
381413 const new_value = mutation ? get ( d ) : runes && bindable ? proxy ( value ) : value ;
@@ -390,10 +422,12 @@ export function prop(props, key, flags, fallback) {
390422 return value ;
391423 }
392424
393- // special case — avoid recalculating the derived if
394- // we're in a teardown function and the prop
395- // was overridden locally
396- if ( is_destroying_effect && overridden ) {
425+ // special case — avoid recalculating the derived if we're in a
426+ // teardown function and the prop was overridden locally, or the
427+ // component was already destroyed (this latter part is necessary
428+ // because `bind:this` can read props after the component has
429+ // been destroyed. TODO simplify `bind:this`
430+ if ( ( is_destroying_effect && overridden ) || ( parent_effect . f & DESTROYED ) !== 0 ) {
397431 return d . v ;
398432 }
399433
0 commit comments