@ -2,16 +2,20 @@ import { DEV } from 'esm-env';
import { subscribe _to _store } from '../../store/utils.js' ;
import { EMPTY _FUNC , run _all } from '../common.js' ;
import { get _descriptor , get _descriptors , is _array } from './utils.js' ;
import { PROPS _CALL _DEFAULT _VALUE , PROPS _IS _IMMUTABLE , PROPS _IS _RUNES } from '../../constants.js' ;
import {
PROPS _IS _LAZY _INITIAL ,
PROPS _IS _IMMUTABLE ,
PROPS _IS _RUNES ,
PROPS _IS _UPDATED
} from '../../constants.js' ;
import { readonly } from './proxy/readonly.js' ;
import { observe , proxy } from './proxy/proxy.js' ;
import { proxy } from './proxy/proxy.js' ;
export const SOURCE = 1 ;
export const DERIVED = 1 << 1 ;
export const EFFECT = 1 << 2 ;
export const PRE _EFFECT = 1 << 3 ;
export const RENDER _EFFECT = 1 << 4 ;
export const SYNC _EFFECT = 1 << 5 ;
const MANAGED = 1 << 6 ;
const UNOWNED = 1 << 7 ;
export const CLEAN = 1 << 8 ;
@ -20,7 +24,7 @@ export const MAYBE_DIRTY = 1 << 10;
export const INERT = 1 << 11 ;
export const DESTROYED = 1 << 12 ;
const IS _EFFECT = EFFECT | PRE _EFFECT | RENDER _EFFECT | SYNC _EFFECT ;
const IS _EFFECT = EFFECT | PRE _EFFECT | RENDER _EFFECT ;
const FLUSH _MICROTASK = 0 ;
const FLUSH _SYNC = 1 ;
@ -536,7 +540,7 @@ function process_microtask() {
* /
export function schedule _effect ( signal , sync ) {
const flags = signal . f ;
if ( sync || ( flags & SYNC _EFFECT ) !== 0 ) {
if ( sync ) {
execute _effect ( signal ) ;
set _signal _status ( signal , CLEAN ) ;
} else {
@ -1284,14 +1288,6 @@ export function invalidate_effect(init) {
return internal _create _effect ( PRE _EFFECT , init , true , current _block , true ) ;
}
/ * *
* @ param { ( ) => void | ( ( ) => void ) } init
* @ returns { import ( './types.js' ) . EffectSignal }
* /
function sync _effect ( init ) {
return internal _create _effect ( SYNC _EFFECT , init , true , current _block , true ) ;
}
/ * *
* @ template { import ( './types.js' ) . Block } B
* @ param { ( block : B ) => void | ( ( ) => void ) } init
@ -1390,124 +1386,114 @@ export function is_store(val) {
/ * *
* This function is responsible for synchronizing a possibly bound prop with the inner component state .
* It is used whenever the compiler sees that the component writes to the prop .
*
* - If the parent passes down a prop without binding , like ` <Component prop={value} /> ` , then create a signal
* that updates whenever the value is updated from the parent or from within the component itself
* - If the parent passes down a prop with a binding , like ` <Component bind:prop={value} /> ` , then
* - if the thing that is passed along is the original signal ( not a property on it ) , and the equality functions
* are equal , then just use that signal , no need to create an intermediate one
* - otherwise create a signal that updates whenever the value is updated from the parent , and when it ' s updated
* from within the component itself , call the setter of the parent which will propagate the value change back
* It is used whenever the compiler sees that the component writes to the prop , or when it has a default prop _value .
* @ template V
* @ param { Record < string , unknown > } props
* @ param { string } key
* @ param { number } flags
* @ param { V | ( ( ) => V ) } [ default_value ]
* @ returns { import ( './types.js' ) . Signal < V > | ( ( ) => V ) }
* @ param { V | ( ( ) => V ) } [ initial ]
* @ returns { ( ( ) => V | ( ( arg : V ) => V ) | ( ( arg : V , mutation : boolean ) => V ) ) }
* /
export function prop _source ( props , key , flags , default _value ) {
const call _default _value = ( flags & PROPS _CALL _DEFAULT _VALUE ) !== 0 ;
const immutable = ( flags & PROPS _IS _IMMUTABLE ) !== 0 ;
const runes = ( flags & PROPS _IS _RUNES ) !== 0 ;
const update _bound _prop = get _descriptor ( props , key ) ? . set ;
let value = props [ key ] ;
const should _set _default _value = value === undefined && default _value !== undefined ;
export function prop ( props , key , flags , initial ) {
var immutable = ( flags & PROPS _IS _IMMUTABLE ) !== 0 ;
var runes = ( flags & PROPS _IS _RUNES ) !== 0 ;
if ( update _bound _prop && runes && default _value !== undefined ) {
var setter = get _descriptor ( props , key ) ? . set ;
if ( DEV && setter && runes && initial !== undefined ) {
// TODO consolidate all these random runtime errors
throw new Error ( 'Cannot use fallback values with bind:' ) ;
}
if ( should _set _default _value ) {
value =
// @ts-expect-error would need a cumbersome method overload to type this
call _default _value ? default _value ( ) : default _value ;
var prop _value = /** @type {V} */ ( props [ key ] ) ;
if ( prop _value === undefined && initial !== undefined ) {
// @ts-expect-error would need a cumbersome method overload to type this
if ( ( flags & PROPS _IS _LAZY _INITIAL ) !== 0 ) initial = initial ( ) ;
if ( DEV && runes ) {
value = readonly ( proxy ( /** @type {any} */ ( value ) ) ) ;
initial = readonly ( proxy ( /** @type {any} */ ( initial ) ) ) ;
}
prop _value = /** @type {V} */ ( initial ) ;
if ( setter ) setter ( prop _value ) ;
}
const source _signal = immutable ? source ( value ) : mutable _source ( value ) ;
var getter = ( ) => {
var value = /** @type {V} */ ( props [ key ] ) ;
if ( value !== undefined ) initial = undefined ;
return value === undefined ? /** @type {V} */ ( initial ) : value ;
} ;
// Synchronize prop changes with source signal.
// Needs special equality checking because the prop in the
// parent could be changed through `foo.bar = 'new value'`.
let ignore _next1 = false ;
let ignore _next2 = false ;
let did _update _to _defined = ! should _set _default _value ;
// easy mode — prop is never written to
if ( ( flags & PROPS _IS _UPDATED ) === 0 ) {
return getter ;
}
let mount = true ;
sync _effect ( ( ) => {
observe ( props ) ;
// intermediate mode — prop is written to, but the parent component had
// `bind:foo` which means we can just call `$$props.foo = value` directly
if ( setter ) {
return function ( /** @type {V} */ value ) {
if ( arguments . length === 1 ) {
/** @type {Function} */ ( setter ) ( value ) ;
return value ;
} else {
return getter ( ) ;
}
} ;
}
// Before if to ensure signal dependency is registered
const propagating _value = props [ key ] ;
if ( mount ) {
mount = false ;
return ;
}
if ( ignore _next1 ) {
ignore _next1 = false ;
return ;
// 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
// 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 ;
}
if (
// Ensure that updates from undefined to undefined are ignored
( did _update _to _defined || propagating _value !== undefined ) &&
not _equal ( immutable , propagating _value , source _signal . v )
) {
ignore _next2 = true ;
did _update _to _defined = true ;
// TODO figure out why we need it this way and the explain in a comment;
// some tests fail is we just do set_signal_value(source_signal, propagating_value)
untrack ( ( ) => set _signal _value ( source _signal , propagating _value ) ) ;
}
was _from _child = false ;
return ( inner _current _value . v = parent _value ) ;
} ) ;
if ( update _bound _prop !== undefined ) {
let ignore _first = ! should _set _default _value ;
sync _effect ( ( ) => {
// Before if to ensure signal dependency is registered
const propagating _value = get ( source _signal ) ;
if ( ignore _first ) {
ignore _first = false ;
return ;
}
if ( ignore _next2 ) {
ignore _next2 = false ;
return ;
}
if ( ! immutable ) current _value . e = safe _equal ;
ignore _next1 = true ;
did _update _to _defined = true ;
untrack ( ( ) => update _bound _prop ( propagating _value ) ) ;
} ) ;
}
return function ( /** @type {V} */ value , mutation = false ) {
var current = get ( current _value ) ;
return /** @type {import('./types.js').Signal<V>} */ ( source _signal ) ;
}
// legacy nonsense — need to ensure the source is invalidated when necessary
if ( is _signals _recorded ) {
// 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 ) ;
}
/ * *
* @ param { boolean } immutable
* @ param { unknown } a
* @ param { unknown } b
* @ returns { boolean }
* /
function not _equal ( immutable , a , b ) {
return immutable ? immutable _not _equal ( a , b ) : safe _not _equal ( a , b ) ;
}
if ( arguments . length > 0 ) {
if ( mutation || ( immutable ? value !== current : safe _not _equal ( value , current ) ) ) {
from _child = true ;
set ( inner _current _value , mutation ? current : value ) ;
get ( current _value ) ; // force a synchronisation immediately
}
/ * *
* @ param { unknown } a
* @ param { unknown } b
* @ returns { boolean }
* /
function immutable _not _equal ( a , b ) {
// eslint-disable-next-line eqeqeq
return a != a ? b == b : a !== b ;
return value ;
}
return current ;
} ;
}
/ * *
@ -1584,82 +1570,67 @@ export function bubble_event($$props, event) {
/ * *
* @ param { import ( './types.js' ) . Signal < number > } signal
* @ param { 1 | - 1 } [ d ]
* @ returns { number }
* /
export function increment( signal ) {
export function update( signal , d = 1 ) {
const value = get ( signal ) ;
set _signal _value ( signal , value + 1 ) ;
set _signal _value ( signal , value + d ) ;
return value ;
}
/ * *
* @ param { import ( './types.js' ) . Store < number > } store
* @ param { number } store _value
* @ returns { number }
* /
export function increment _store ( store , store _value ) {
store . set ( store _value + 1 ) ;
return store _value ;
}
/ * *
* @ param { import ( './types.js' ) . Signal < number > } signal
* @ param { ( ( value ? : number ) => number ) } fn
* @ param { 1 | - 1 } [ d ]
* @ returns { number }
* /
export function decrement( signal ) {
const value = get( signal ) ;
set_signal _value ( signal , value - 1 ) ;
export function update _prop ( fn , d = 1 ) {
const value = fn ( ) ;
fn ( value + d ) ;
return value ;
}
/ * *
* @ param { import ( './types.js' ) . Store < number > } store
* @ param { number } store _value
* @ param { 1 | - 1 } [ d ]
* @ returns { number }
* /
export function decrement _store ( store , store _value ) {
store . set ( store _value - 1 ) ;
export function up dat e_store ( store , store _value , d = 1 ) {
store . set ( store _value + d ) ;
return store _value ;
}
/ * *
* @ param { import ( './types.js' ) . Signal < number > } signal
* @ param { 1 | - 1 } [ d ]
* @ returns { number }
* /
export function increment_pre ( signal ) {
const value = get ( signal ) + 1 ;
export function update_pre ( signal , d = 1 ) {
const value = get ( signal ) + d ;
set _signal _value ( signal , value ) ;
return value ;
}
/ * *
* @ param { import ( './types.js' ) . Store < number > } store
* @ param { number } store _value
* @ returns { number }
* /
export function increment _pre _store ( store , store _value ) {
const value = store _value + 1 ;
store . set ( value ) ;
return value ;
}
/ * *
* @ param { import ( './types.js' ) . Signal < number > } signal
* @ param { ( ( value ? : number ) => number ) } fn
* @ param { 1 | - 1 } [ d ]
* @ returns { number }
* /
export function decrement_pre ( signal ) {
const value = get( signal ) - 1 ;
set_signal _value ( signal , value ) ;
export function update _pre _prop ( fn , d = 1 ) {
const value = fn ( ) + d ;
fn ( value ) ;
return value ;
}
/ * *
* @ param { import ( './types.js' ) . Store < number > } store
* @ param { number } store _value
* @ param { 1 | - 1 } [ d ]
* @ returns { number }
* /
export function decrement _pre _store ( store , store _value ) {
const value = store _value - 1 ;
export function up dat e_pre _store ( store , store _value , d = 1 ) {
const value = store _value + d ;
store . set ( value ) ;
return value ;
}