@@ -8,12 +8,11 @@ import {
8
8
PROPS_IS_UPDATED
9
9
} from '../../../constants.js' ;
10
10
import { 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' ;
12
12
import { 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' ;
15
14
import * 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' ;
17
16
import { proxy } from '../proxy.js' ;
18
17
import { capture_store_binding } from './store.js' ;
19
18
import { legacy_mode_flag } from '../../flags/index.js' ;
@@ -260,89 +259,92 @@ function has_destroyed_component_ctx(current_value) {
260
259
* @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V)) }
261
260
*/
262
261
export function prop ( props , key , flags , fallback ) {
263
- var immutable = ( flags & PROPS_IS_IMMUTABLE ) !== 0 ;
264
262
var runes = ! legacy_mode_flag || ( flags & PROPS_IS_RUNES ) !== 0 ;
265
263
var bindable = ( flags & PROPS_IS_BINDABLE ) !== 0 ;
266
264
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 ;
285
265
286
266
var fallback_value = /** @type {V } */ ( fallback ) ;
287
267
var fallback_dirty = true ;
288
- var fallback_used = false ;
289
268
290
269
var get_fallback = ( ) => {
291
- fallback_used = true ;
292
270
if ( fallback_dirty ) {
293
271
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 ) ;
299
276
}
300
277
301
278
return fallback_value ;
302
279
} ;
303
280
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 ;
308
283
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
+ }
311
310
}
312
311
313
312
/** @type {() => V } */
314
313
var getter ;
314
+
315
315
if ( runes ) {
316
316
getter = ( ) => {
317
317
var value = /** @type {V } */ ( props [ key ] ) ;
318
318
if ( value === undefined ) return get_fallback ( ) ;
319
319
fallback_dirty = true ;
320
- fallback_used = false ;
321
320
return value ;
322
321
} ;
323
322
} 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 ;
330
323
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
+
333
334
return value === undefined ? fallback_value : value ;
334
335
} ;
335
336
}
336
337
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 ) {
339
340
return getter ;
340
341
}
341
342
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
344
345
if ( setter ) {
345
346
var legacy_parent = props . $$legacy ;
347
+
346
348
return function ( /** @type {any } */ value , /** @type {boolean } */ mutation ) {
347
349
if ( arguments . length > 0 ) {
348
350
// 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) {
352
354
if ( ! runes || ! mutation || legacy_parent || is_store_sub ) {
353
355
/** @type {Function } */ ( setter ) ( mutation ? getter ( ) : value ) ;
354
356
}
357
+
355
358
return value ;
356
- } else {
357
- return getter ( ) ;
358
359
}
360
+
361
+ return getter ( ) ;
359
362
} ;
360
363
}
361
364
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 ) ;
389
368
390
- if ( ! immutable ) current_value . equals = safe_equals ;
369
+ // Capture the initial value if it's bindable
370
+ if ( bindable ) get ( d ) ;
391
371
392
372
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
-
405
373
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 ;
416
375
417
- if ( has_destroyed_component_ctx ( current_value ) ) {
418
- return value ;
419
- }
376
+ set ( d , new_value ) ;
420
377
421
- untrack ( ( ) => get ( current_value ) ) ; // force a synchronisation immediately
378
+ if ( fallback_value !== undefined ) {
379
+ fallback_value = new_value ;
422
380
}
423
381
424
382
return value ;
425
383
}
426
384
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 ;
429
388
}
430
389
431
- return get ( current_value ) ;
390
+ return get ( d ) ;
432
391
} ;
433
392
}
0 commit comments