@@ -8,12 +8,11 @@ import {
88 PROPS_IS_UPDATED
99} from '../../../constants.js' ;
1010import { get_descriptor , is_function } from '../../shared/utils.js' ;
11- import { mutable_source , set , source , update } from './sources.js' ;
11+ import { set , source , update } from './sources.js' ;
1212import { derived , derived_safe_equal } from './deriveds.js' ;
13- import { get , captured_signals , untrack } from '../runtime.js' ;
14- import { safe_equals } from './equality.js' ;
13+ import { get , untrack } from '../runtime.js' ;
1514import * as e from '../errors.js' ;
16- import { LEGACY_DERIVED_PROP , LEGACY_PROPS , STATE_SYMBOL } from '#client/constants' ;
15+ import { LEGACY_PROPS , STATE_SYMBOL } from '#client/constants' ;
1716import { proxy } from '../proxy.js' ;
1817import { capture_store_binding } from './store.js' ;
1918import { legacy_mode_flag } from '../../flags/index.js' ;
@@ -260,89 +259,92 @@ function has_destroyed_component_ctx(current_value) {
260259 * @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V)) }
261260 */
262261export function prop ( props , key , flags , fallback ) {
263- var immutable = ( flags & PROPS_IS_IMMUTABLE ) !== 0 ;
264262 var runes = ! legacy_mode_flag || ( flags & PROPS_IS_RUNES ) !== 0 ;
265263 var bindable = ( flags & PROPS_IS_BINDABLE ) !== 0 ;
266264 var lazy = ( flags & PROPS_IS_LAZY_INITIAL ) !== 0 ;
267- var is_store_sub = false ;
268- var prop_value ;
269-
270- if ( bindable ) {
271- [ prop_value , is_store_sub ] = capture_store_binding ( ( ) => /** @type {V } */ ( props [ key ] ) ) ;
272- } else {
273- prop_value = /** @type {V } */ ( props [ key ] ) ;
274- }
275-
276- // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})`
277- // or `createClassComponent(Component, props)`
278- var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props ;
279-
280- var setter =
281- ( bindable &&
282- ( get_descriptor ( props , key ) ?. set ??
283- ( is_entry_props && key in props && ( ( v ) => ( props [ key ] = v ) ) ) ) ) ||
284- undefined ;
285265
286266 var fallback_value = /** @type {V } */ ( fallback ) ;
287267 var fallback_dirty = true ;
288- var fallback_used = false ;
289268
290269 var get_fallback = ( ) => {
291- fallback_used = true ;
292270 if ( fallback_dirty ) {
293271 fallback_dirty = false ;
294- if ( lazy ) {
295- fallback_value = untrack ( /** @type {() => V } */ ( fallback ) ) ;
296- } else {
297- fallback_value = /** @type {V } */ ( fallback ) ;
298- }
272+
273+ fallback_value = lazy
274+ ? untrack ( /** @type {() => V } */ ( fallback ) )
275+ : /** @type {V } */ ( fallback ) ;
299276 }
300277
301278 return fallback_value ;
302279 } ;
303280
304- if ( prop_value === undefined && fallback !== undefined ) {
305- if ( setter && runes ) {
306- e . props_invalid_value ( key ) ;
307- }
281+ /** @type {((v: V) => void) | undefined } */
282+ var setter ;
308283
309- prop_value = get_fallback ( ) ;
310- if ( setter ) setter ( prop_value ) ;
284+ if ( bindable ) {
285+ // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})`
286+ // or `createClassComponent(Component, props)`
287+ var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props ;
288+
289+ setter =
290+ get_descriptor ( props , key ) ?. set ??
291+ ( is_entry_props && key in props ? ( v ) => ( props [ key ] = v ) : undefined ) ;
292+ }
293+
294+ var initial_value ;
295+ var is_store_sub = false ;
296+
297+ if ( bindable ) {
298+ [ initial_value , is_store_sub ] = capture_store_binding ( ( ) => /** @type {V } */ ( props [ key ] ) ) ;
299+ } else {
300+ initial_value = /** @type {V } */ ( props [ key ] ) ;
301+ }
302+
303+ if ( initial_value === undefined && fallback !== undefined ) {
304+ initial_value = get_fallback ( ) ;
305+
306+ if ( setter ) {
307+ if ( runes ) e . props_invalid_value ( key ) ;
308+ setter ( initial_value ) ;
309+ }
311310 }
312311
313312 /** @type {() => V } */
314313 var getter ;
314+
315315 if ( runes ) {
316316 getter = ( ) => {
317317 var value = /** @type {V } */ ( props [ key ] ) ;
318318 if ( value === undefined ) return get_fallback ( ) ;
319319 fallback_dirty = true ;
320- fallback_used = false ;
321320 return value ;
322321 } ;
323322 } else {
324- // Svelte 4 did not trigger updates when a primitive value was updated to the same value.
325- // Replicate that behavior through using a derived
326- var derived_getter = ( immutable ? derived : derived_safe_equal ) (
327- ( ) => /** @type {V } */ ( props [ key ] )
328- ) ;
329- derived_getter . f |= LEGACY_DERIVED_PROP ;
330323 getter = ( ) => {
331- var value = get ( derived_getter ) ;
332- if ( value !== undefined ) fallback_value = /** @type {V } */ ( undefined ) ;
324+ var value = /** @type {V } */ ( props [ key ] ) ;
325+
326+ if ( value !== undefined ) {
327+ // in legacy mode, we don't revert to the fallback value
328+ // if the prop goes from defined to undefined. The easiest
329+ // way to model this is to make the fallback undefined
330+ // as soon as the prop has a value
331+ fallback_value = /** @type {V } */ ( undefined ) ;
332+ }
333+
333334 return value === undefined ? fallback_value : value ;
334335 } ;
335336 }
336337
337- // easy mode — prop is never written to
338- if ( ( flags & PROPS_IS_UPDATED ) === 0 && runes ) {
338+ // prop is never written to — we only need a getter
339+ if ( ( flags & PROPS_IS_UPDATED ) === 0 ) {
339340 return getter ;
340341 }
341342
342- // intermediate mode — prop is written to, but the parent component had
343- // `bind:foo` which means we can just call `$$props.foo = value` directly
343+ // prop is written to, but the parent component had `bind:foo` which
344+ // means we can just call `$$props.foo = value` directly
344345 if ( setter ) {
345346 var legacy_parent = props . $$legacy ;
347+
346348 return function ( /** @type {any } */ value , /** @type {boolean } */ mutation ) {
347349 if ( arguments . length > 0 ) {
348350 // We don't want to notify if the value was mutated and the parent is in runes mode.
@@ -352,82 +354,39 @@ export function prop(props, key, flags, fallback) {
352354 if ( ! runes || ! mutation || legacy_parent || is_store_sub ) {
353355 /** @type {Function } */ ( setter ) ( mutation ? getter ( ) : value ) ;
354356 }
357+
355358 return value ;
356- } else {
357- return getter ( ) ;
358359 }
360+
361+ return getter ( ) ;
359362 } ;
360363 }
361364
362- // hard mode. this is where it gets ugly — the value in the child should
363- // synchronize with the parent, but it should also be possible to temporarily
364- // set the value to something else locally.
365- var from_child = false ;
366- var was_from_child = false ;
367-
368- // The derived returns the current value. The underlying mutable
369- // source is written to from various places to persist this value.
370- var inner_current_value = mutable_source ( prop_value ) ;
371- var current_value = derived ( ( ) => {
372- var parent_value = getter ( ) ;
373- var child_value = get ( inner_current_value ) ;
374-
375- if ( from_child ) {
376- from_child = false ;
377- was_from_child = true ;
378- return child_value ;
379- }
380-
381- was_from_child = false ;
382- return ( inner_current_value . v = parent_value ) ;
383- } ) ;
384-
385- // Ensure we eagerly capture the initial value if it's bindable
386- if ( bindable ) {
387- get ( current_value ) ;
388- }
365+ // prop is written to, but there's no binding, which means we
366+ // create a derived that we can write to locally
367+ var d = ( ( flags & PROPS_IS_IMMUTABLE ) !== 0 ? derived : derived_safe_equal ) ( getter ) ;
389368
390- if ( ! immutable ) current_value . equals = safe_equals ;
369+ // Capture the initial value if it's bindable
370+ if ( bindable ) get ( d ) ;
391371
392372 return function ( /** @type {any } */ value , /** @type {boolean } */ mutation ) {
393- // legacy nonsense — need to ensure the source is invalidated when necessary
394- // also needed for when handling inspect logic so we can inspect the correct source signal
395- if ( captured_signals !== null ) {
396- // set this so that we don't reset to the parent value if `d`
397- // is invalidated because of `invalidate_inner_signals` (rather
398- // than because the parent or child value changed)
399- from_child = was_from_child ;
400- // invoke getters so that signals are picked up by `invalidate_inner_signals`
401- getter ( ) ;
402- get ( inner_current_value ) ;
403- }
404-
405373 if ( arguments . length > 0 ) {
406- const new_value = mutation ? get ( current_value ) : runes && bindable ? proxy ( value ) : value ;
407-
408- if ( ! current_value . equals ( new_value ) ) {
409- from_child = true ;
410- set ( inner_current_value , new_value ) ;
411- // To ensure the fallback value is consistent when used with proxies, we
412- // update the local fallback_value, but only if the fallback is actively used
413- if ( fallback_used && fallback_value !== undefined ) {
414- fallback_value = new_value ;
415- }
374+ const new_value = mutation ? get ( d ) : runes && bindable ? proxy ( value ) : value ;
416375
417- if ( has_destroyed_component_ctx ( current_value ) ) {
418- return value ;
419- }
376+ set ( d , new_value ) ;
420377
421- untrack ( ( ) => get ( current_value ) ) ; // force a synchronisation immediately
378+ if ( fallback_value !== undefined ) {
379+ fallback_value = new_value ;
422380 }
423381
424382 return value ;
425383 }
426384
427- if ( has_destroyed_component_ctx ( current_value ) ) {
428- return current_value . v ;
385+ // TODO is this still necessary post-#16263?
386+ if ( has_destroyed_component_ctx ( d ) ) {
387+ return d . v ;
429388 }
430389
431- return get ( current_value ) ;
390+ return get ( d ) ;
432391 } ;
433392}
0 commit comments