Skip to content

Commit

Permalink
lib/signal - make js-private properties protected instead
Browse files Browse the repository at this point in the history
  • Loading branch information
LankyMoose committed Oct 13, 2024
1 parent 90251e5 commit 3731fbd
Showing 1 changed file with 37 additions and 30 deletions.
67 changes: 37 additions & 30 deletions packages/lib/src/signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,15 @@ class WatchEffect {
this.subs = new Map()
this.isRunning = false
if (__DEV__) {
this[$HMR_ACCEPT] = {
provide: () => this,
inject: (prev) => {
if (prev.isRunning) return
this.stop()
},
destroy: () => {
this.stop()
},
this[$HMR_ACCEPT] = {
provide: () => this,
inject: (prev) => {
if (prev.isRunning) return
this.stop()
},
destroy: () => {
this.stop()
},
}
}
}
Expand All @@ -134,6 +134,13 @@ class WatchEffect {
}

this.isRunning = true
/**
* A tighter integration with HMR could see us
* only needing to delay this during an HMR update.
*
* We postpone the callback until the next tick so that HMR
* can persist any referenced signals.
*/
queueMicrotask(() => {
if (this.isRunning) {
this.cleanup = appliedTrackedEffects(this.getter, this.subs, this.id)
Expand Down Expand Up @@ -200,13 +207,13 @@ export type SignalSubscriber =

export class Signal<T> {
[$SIGNAL] = true
#value: T
#subscribers = new Set<SignalSubscriber>()
#getter?: () => T
protected $value: T
protected $subscribers = new Set<SignalSubscriber>()
protected $getter?: () => T
displayName?: string;
[$HMR_ACCEPT]?: HMRAccept<Signal<any>>
constructor(initial: T, displayName?: string) {
this.#value = initial
this.$value = initial
if (displayName) this.displayName = displayName
if (__DEV__) {
this[$HMR_ACCEPT] = {
Expand Down Expand Up @@ -263,37 +270,37 @@ export class Signal<T> {

get value() {
onSignalValueObserved(this)
return this.#value
return this.$value
}

set value(next: T) {
this.#value = next
this.$value = next
this.notify()
}

peek() {
return this.#value
return this.$value
}

sneak(newValue: T) {
this.#value = newValue
this.$value = newValue
}

toString() {
onSignalValueObserved(this)
return `${this.#value}`
return `${this.$value}`
}

subscribe(cb: (state: T) => void): () => void {
this.#subscribers.add(cb)
return () => (this.#subscribers.delete(cb), void 0)
this.$subscribers.add(cb)
return () => (this.$subscribers.delete(cb), void 0)
}

notify(options?: { filter?: (sub: Function | Kaioken.VNode) => boolean }) {
this.#subscribers.forEach((sub) => {
this.$subscribers.forEach((sub) => {
if (options?.filter && !options.filter(sub)) return
if (sub instanceof Function) {
return sub(this.#value)
return sub(this.$value)
}
getVNodeAppContext(sub).requestUpdate(sub)
})
Expand All @@ -304,24 +311,24 @@ export class Signal<T> {
}

static unsubscribe(sub: SignalSubscriber, signal: Signal<any>) {
signal.#subscribers.delete(sub)
signal.$subscribers.delete(sub)
}

static subscribers(signal: Signal<any>) {
return signal.#subscribers
return signal.$subscribers
}

static makeReadonly<T>(
signal: Signal<T>,
getter?: () => T
): ReadonlySignal<T> {
const desc = Object.getOwnPropertyDescriptor(signal, "value")
signal.#getter = getter
signal.$getter = getter
if (desc && !desc.writable) return signal
return Object.defineProperty(signal, "value", {
get: function (this: Signal<T>) {
onSignalValueObserved(this)
return this.#value
return this.$value
},
configurable: true,
})
Expand All @@ -332,19 +339,19 @@ export class Signal<T> {
return Object.defineProperty(signal, "value", {
get: function (this: Signal<T>) {
onSignalValueObserved(this)
return this.#value
return this.$value
},
set: function (this: Signal<T>, value) {
this.#value = value
this.$value = value
this.notify()
},
configurable: true,
})
}
static getComputedGetter<T>(signal: Signal<T>) {
if (!signal.#getter)
if (!signal.$getter)
throw new Error("attempted to get computed getter on non-computed signal")
return signal.#getter
return signal.$getter
}
}

Expand Down

0 comments on commit 3731fbd

Please sign in to comment.