From 3731fbd83c09d0aaabf827a517b0d6bc9632d1b2 Mon Sep 17 00:00:00 2001 From: Robby6Strings Date: Sun, 13 Oct 2024 21:49:31 +1300 Subject: [PATCH] lib/signal - make js-private properties protected instead --- packages/lib/src/signal.ts | 67 +++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/packages/lib/src/signal.ts b/packages/lib/src/signal.ts index 202730b..5ccd22f 100644 --- a/packages/lib/src/signal.ts +++ b/packages/lib/src/signal.ts @@ -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() + }, } } } @@ -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) @@ -200,13 +207,13 @@ export type SignalSubscriber = export class Signal { [$SIGNAL] = true - #value: T - #subscribers = new Set() - #getter?: () => T + protected $value: T + protected $subscribers = new Set() + protected $getter?: () => T displayName?: string; [$HMR_ACCEPT]?: HMRAccept> constructor(initial: T, displayName?: string) { - this.#value = initial + this.$value = initial if (displayName) this.displayName = displayName if (__DEV__) { this[$HMR_ACCEPT] = { @@ -263,37 +270,37 @@ export class Signal { 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) }) @@ -304,11 +311,11 @@ export class Signal { } static unsubscribe(sub: SignalSubscriber, signal: Signal) { - signal.#subscribers.delete(sub) + signal.$subscribers.delete(sub) } static subscribers(signal: Signal) { - return signal.#subscribers + return signal.$subscribers } static makeReadonly( @@ -316,12 +323,12 @@ export class Signal { getter?: () => T ): ReadonlySignal { 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) { onSignalValueObserved(this) - return this.#value + return this.$value }, configurable: true, }) @@ -332,19 +339,19 @@ export class Signal { return Object.defineProperty(signal, "value", { get: function (this: Signal) { onSignalValueObserved(this) - return this.#value + return this.$value }, set: function (this: Signal, value) { - this.#value = value + this.$value = value this.notify() }, configurable: true, }) } static getComputedGetter(signal: Signal) { - if (!signal.#getter) + if (!signal.$getter) throw new Error("attempted to get computed getter on non-computed signal") - return signal.#getter + return signal.$getter } }