Skip to content

Instantly share code, notes, and snippets.

@johnsoncodehk
Created March 28, 2025 05:08
Show Gist options
  • Save johnsoncodehk/ddf9c8f619a937522d842f7c1669c839 to your computer and use it in GitHub Desktop.
Save johnsoncodehk/ddf9c8f619a937522d842f7c1669c839 to your computer and use it in GitHub Desktop.
import { createReactiveSystem, Dependency, Link, Subscriber, SubscriberFlags } from 'alien-signals/esm';
import { ReactiveFramework } from "../util/reactiveFramework";
let toCleanup: (() => void)[] = [];
export const alienSignalsStaticDeps: ReactiveFramework = {
type: "pure",
name: "alien-signals (static deps)",
signal: (initial) => {
const data = signal(initial);
return data;
},
computed: (deps, fn) => {
const c = computed(() => fn(...deps.map(dep => dep.value)));
for (const dep of deps) {
link(dep, c);
}
return c;
},
effect: (deps, fn) => {
const e = effect(() => fn(...deps.map(dep => dep.value)));
for (const dep of deps) {
link(dep, e);
}
toCleanup.push(e.stop.bind(e));
},
withBatch: (fn) => {
startBatch();
fn();
endBatch();
},
withBuild: (fn) => fn(),
cleanup: () => {
for (const cleanup of toCleanup) {
cleanup();
}
toCleanup = [];
},
};
const {
link,
propagate,
endTracking,
startTracking,
updateDirtyFlag,
processComputedUpdate,
processEffectNotifications,
} = createReactiveSystem({
updateComputed(computed: Computed) {
return computed.update();
},
notifyEffect(effect: Effect) {
effect.notify();
return true;
},
});
let batchDepth = 0;
function startBatch(): void {
++batchDepth;
}
function endBatch(): void {
if (!--batchDepth) {
processEffectNotifications();
}
}
function signal<T>(): Signal<T | undefined>;
function signal<T>(oldValue: T): Signal<T>;
function signal<T>(oldValue?: T): Signal<T | undefined> {
return new Signal(oldValue);
}
class Signal<T = any> implements Dependency {
// Dependency fields
subs: Link | undefined = undefined;
subsTail: Link | undefined = undefined;
constructor(
public currentValue: T
) { }
get value(): T {
return this.currentValue;
}
set value(value: T) {
if (this.currentValue !== value) {
this.currentValue = value;
const subs = this.subs;
if (subs !== undefined) {
propagate(subs);
if (!batchDepth) {
processEffectNotifications();
}
}
}
}
}
function computed<T>(getter: () => T): Computed<T> {
return new Computed<T>(getter);
}
class Computed<T = any> implements Subscriber, Dependency {
currentValue: T | undefined = undefined;
// Dependency fields
subs: Link | undefined = undefined;
subsTail: Link | undefined = undefined;
// Subscriber fields
deps: Link | undefined = undefined;
depsTail: Link | undefined = undefined;
flags: SubscriberFlags = SubscriberFlags.Computed | SubscriberFlags.Dirty;
constructor(
public getter: () => T
) { }
get value(): T {
const flags = this.flags;
if (flags & (SubscriberFlags.PendingComputed | SubscriberFlags.Dirty)) {
processComputedUpdate(this, flags);
}
return this.currentValue!;
}
update(): boolean {
this.flags &= ~(SubscriberFlags.Notified | SubscriberFlags.Recursed | SubscriberFlags.Propagated);
const oldValue = this.currentValue;
const newValue = this.getter();
if (oldValue !== newValue) {
this.currentValue = newValue;
return true;
}
return false;
}
}
function effect<T>(fn: () => T): Effect<T> {
const e = new Effect(fn);
e.run();
return e;
}
class Effect<T = any> implements Subscriber {
// Subscriber fields
deps: Link | undefined = undefined;
depsTail: Link | undefined = undefined;
flags: SubscriberFlags = SubscriberFlags.Effect;
constructor(
public run: () => T
) { }
notify(): void {
const flags = this.flags;
if (
flags & SubscriberFlags.Dirty
|| (flags & SubscriberFlags.PendingComputed && updateDirtyFlag(this, flags))
) {
this.flags &= ~(SubscriberFlags.Notified | SubscriberFlags.Recursed | SubscriberFlags.Propagated);
this.run();
}
}
stop(): void {
startTracking(this);
endTracking(this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment