Skip to content

Commit

Permalink
chore: fix ci
Browse files Browse the repository at this point in the history
  • Loading branch information
crutchcorn committed Feb 7, 2024
1 parent f0ec935 commit 790303c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 91 deletions.
91 changes: 46 additions & 45 deletions packages/store/src/derived.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
import {Store} from "./store";
import {Listener} from "./types";

interface DerivedOptions<
TState
> {
onSubscribe?: (
listener: Listener,
derived: Derived<TState>,
) => () => void
import { Store } from './store'
import type { Listener } from './types'

interface DerivedOptions<TState> {
onSubscribe?: (listener: Listener, derived: Derived<TState>) => () => void
onUpdate?: () => void
}

type Deps = Array<Derived<any> | Store<any>>;
type Deps = Array<Derived<any> | Store<any>>

export class Derived<
TState
> {
export class Derived<TState> {
listeners = new Set<Listener>()
state: TState
options?: DerivedOptions<TState>

rootStores: Set<Store<unknown>> = new Set();
deps: Deps;
rootStores: Set<Store<unknown>> = new Set()
deps: Deps

// Functions representing the subscriptions. Call a function to cleanup
_subscriptions: Array<() => void> = [];
_subscriptions: Array<() => void> = []

constructor(deps: Deps, fn: () => TState, options?: DerivedOptions<TState>) {
this.options = options;
this.deps = deps;
this.state = fn();
this.options = options
this.deps = deps
this.state = fn()
/**
* This is here to solve the pyramid dependency problem where:
* A
Expand All @@ -45,48 +38,56 @@ export class Derived<
*
* This is a record of stores, because derived stores are not able to write values to, but stores are
*/
const storeToDerived: Map<Store<unknown>, Set<Derived<unknown>>> = new Map();
const derivedToStore: Map<Derived<unknown>, Set<Store<unknown>>> = new Map();

const updateStoreToDerived = (store: Store<unknown>, dep: Derived<unknown>) => {
const prevDerivesForStore = storeToDerived.get(store) || new Set();
prevDerivesForStore.add(dep);
const storeToDerived: Map<Store<unknown>, Set<Derived<unknown>>> = new Map()
const derivedToStore: Map<Derived<unknown>, Set<Store<unknown>>> = new Map()

const updateStoreToDerived = (
store: Store<unknown>,
dep: Derived<unknown>,
) => {
const prevDerivesForStore = storeToDerived.get(store) || new Set()
prevDerivesForStore.add(dep)
storeToDerived.set(store, prevDerivesForStore)
}
deps.forEach(dep => {
deps.forEach((dep) => {
if (dep instanceof Derived) {
derivedToStore.set(dep, dep.rootStores)
dep.rootStores.forEach(store => {
this.rootStores.add(store);
dep.rootStores.forEach((store) => {
this.rootStores.add(store)
updateStoreToDerived(store, dep)
})
} else if (dep instanceof Store) {
this.rootStores.add(dep);
this.rootStores.add(dep)
updateStoreToDerived(dep, this as Derived<unknown>)
}
})

let __depsThatHaveWrittenThisTick: Deps = [];
let __depsThatHaveWrittenThisTick: Deps = []

deps.forEach(dep => {
let relatedLinkedDerivedVals: null | Set<Derived<unknown>> = null;
const stores = (dep instanceof Derived ? derivedToStore.get(dep) : new Set([dep])) ?? new Set();
stores.forEach(store => {
deps.forEach((dep) => {
let relatedLinkedDerivedVals: null | Set<Derived<unknown>> = null
const stores =
(dep instanceof Derived ? derivedToStore.get(dep) : new Set([dep])) ??
new Set()
stores.forEach((store) => {
// Only runs on first loop through the store
if (!relatedLinkedDerivedVals) relatedLinkedDerivedVals = new Set();
storeToDerived.get(store)?.forEach(derived => {
if (!relatedLinkedDerivedVals) relatedLinkedDerivedVals = new Set()
storeToDerived.get(store)?.forEach((derived) => {
relatedLinkedDerivedVals!.add(derived)
})
})

const unsub = dep.subscribe(() => {
__depsThatHaveWrittenThisTick.push(dep);
if (!relatedLinkedDerivedVals || __depsThatHaveWrittenThisTick.length === relatedLinkedDerivedVals.size) {
__depsThatHaveWrittenThisTick.push(dep)
if (
!relatedLinkedDerivedVals ||
__depsThatHaveWrittenThisTick.length === relatedLinkedDerivedVals.size
) {
// Yay! All deps are resolved - write the value of this derived
this._setState(fn())
// Cleanup the deps that have written this tick
__depsThatHaveWrittenThisTick = [];
return;
__depsThatHaveWrittenThisTick = []
return
}
})

Expand All @@ -95,11 +96,11 @@ export class Derived<
}

cleanup = () => {
this._subscriptions.forEach(cleanup => cleanup())
}
this._subscriptions.forEach((cleanup) => cleanup())
};

[Symbol.dispose]() {
this.cleanup();
this.cleanup()
}

subscribe = (listener: Listener) => {
Expand All @@ -112,7 +113,7 @@ export class Derived<
}

_setState = (val: TState) => {
this.state = val;
this.state = val
this.options?.onUpdate?.()
this._flush()
}
Expand Down
6 changes: 3 additions & 3 deletions packages/store/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./derived"
export * from "./store"
export * from "./types"
export * from './derived'
export * from './store'
export * from './types'
2 changes: 1 addition & 1 deletion packages/store/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {AnyUpdater, Listener} from "./types";
import type { AnyUpdater, Listener } from './types'

interface StoreOptions<
TState,
Expand Down
29 changes: 20 additions & 9 deletions packages/store/src/tests/derived.bench.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ import { Derived } from '../derived'
import { describe, bench } from 'vitest'
import { computed as vueComputed, ref, watch } from 'vue'
import { createEffect, createMemo, createSignal } from 'solid-js'
import { signal as preactSignal, computed as preactComputed, effect as preactEffect } from '@preact/signals'
import { signal as angularSignal, computed as angularComputed } from '@angular/core'
import {
signal as preactSignal,
computed as preactComputed,
effect as preactEffect,
} from '@preact/signals'
import {
signal as angularSignal,
computed as angularComputed,
} from '@angular/core'
import { createWatch } from '@angular/core/primitives/signals'

function noop(val: any) {
Expand Down Expand Up @@ -84,18 +91,22 @@ describe('Derived', () => {
a.value = 2
})

bench("Angular", () => {
const a = angularSignal(1);
bench('Angular', () => {
const a = angularSignal(1)
const b = angularComputed(() => a())
const c = angularComputed(() => a())
const d = angularComputed(() => b())
const e = angularComputed(() => b())
const f = angularComputed(() => c())
const g = angularComputed(() => d() + e() + f());

createWatch(() => {
console.log(g());
}, () => {}, false)
const g = angularComputed(() => d() + e() + f())

createWatch(
() => {
console.log(g())
},
() => {},
false,
)

a.set(2)
})
Expand Down
63 changes: 31 additions & 32 deletions packages/store/src/tests/derived.test.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import {Store} from "../store";
import {Derived} from "../derived";
import {afterEach, expect, vi} from "vitest";
import { Store } from '../store'
import { Derived } from '../derived'
import { afterEach, expect, vi } from 'vitest'

function viFnSubscribe(subscribable: Store<any> | Derived<any>) {
const fn = vi.fn();
const cleanup = subscribable.subscribe(() => fn(subscribable.state));
const fn = vi.fn()
const cleanup = subscribable.subscribe(() => fn(subscribable.state))
afterEach(() => {
cleanup()
})
return fn;
return fn
}

describe('Derived', () => {
test("Diamond dep problem", () => {
const count = new Store(10);
test('Diamond dep problem', () => {
const count = new Store(10)

const halfCount = new Derived([count], () => {
return count.state / 2;
return count.state / 2
})

const doubleCount = new Derived([count], () => {
return count.state * 2;
return count.state * 2
})

const sumDoubleHalfCount = new Derived([halfCount, doubleCount], () => {
return halfCount.state + doubleCount.state;
return halfCount.state + doubleCount.state
})

const halfCountFn = viFnSubscribe(halfCount);
const doubleCountFn = viFnSubscribe(doubleCount);
const sumDoubleHalfCountFn = viFnSubscribe(sumDoubleHalfCount);
const halfCountFn = viFnSubscribe(halfCount)
const doubleCountFn = viFnSubscribe(doubleCount)
const sumDoubleHalfCountFn = viFnSubscribe(sumDoubleHalfCount)

count.setState(() => 20)

Expand All @@ -54,22 +54,22 @@ describe('Derived', () => {
* \ /
* G
*/
test("Complex diamond dep problem", () => {
const a = new Store(1);
test('Complex diamond dep problem', () => {
const a = new Store(1)
const b = new Derived([a], () => a.state)
const c = new Derived([a], () => a.state)
const d = new Derived([b], () => b.state)
const e = new Derived([b], () => b.state)
const f = new Derived([c], () => c.state)
const g = new Derived([d, e, f], () => d.state + e.state + f.state);
const g = new Derived([d, e, f], () => d.state + e.state + f.state)

const aFn = viFnSubscribe(a);
const bFn = viFnSubscribe(b);
const cFn = viFnSubscribe(c);
const dFn = viFnSubscribe(d);
const eFn = viFnSubscribe(e);
const fFn = viFnSubscribe(f);
const gFn = viFnSubscribe(g);
const aFn = viFnSubscribe(a)
const bFn = viFnSubscribe(b)
const cFn = viFnSubscribe(c)
const dFn = viFnSubscribe(d)
const eFn = viFnSubscribe(e)
const fFn = viFnSubscribe(f)
const gFn = viFnSubscribe(g)

a.setState(() => 2)

Expand All @@ -82,20 +82,19 @@ describe('Derived', () => {
expect(gFn).toHaveBeenNthCalledWith(1, 6)
})


test("Derive from store and another derived", () => {
const count = new Store(10);
test('Derive from store and another derived', () => {
const count = new Store(10)

const doubleCount = new Derived([count], () => {
return count.state * 2;
return count.state * 2
})

const tripleCount = new Derived([count, doubleCount], () => {
return count.state + doubleCount.state;
return count.state + doubleCount.state
})

const doubleCountFn = viFnSubscribe(doubleCount);
const tripleCountFn = viFnSubscribe(tripleCount);
const doubleCountFn = viFnSubscribe(doubleCount)
const tripleCountFn = viFnSubscribe(tripleCount)

count.setState(() => 20)

Expand All @@ -107,4 +106,4 @@ describe('Derived', () => {
expect(doubleCountFn).toHaveBeenNthCalledWith(2, 60)
expect(tripleCountFn).toHaveBeenNthCalledWith(2, 90)
})
});
})
2 changes: 1 addition & 1 deletion packages/store/src/tests/store.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ describe('store', () => {
expect(store.state).toEqual(4)
// Listener is called 4 times because of a lack of batching
expect(listener).toHaveBeenCalledTimes(5)
unsub();
unsub()
})
})

0 comments on commit 790303c

Please sign in to comment.