From ba8f00ca26a1c6d77065990992356ca00000bf82 Mon Sep 17 00:00:00 2001 From: daishi Date: Wed, 11 Dec 2024 20:49:44 +0900 Subject: [PATCH 1/8] wip: dev store with unstable_derive --- src/vanilla/store.ts | 118 +++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 422c4ee99f..92822267b2 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -251,7 +251,7 @@ type DevStoreRev4 = { dev4_restore_atoms: (values: Iterable) => void } -type PrdStore = { +type Store = { get: (atom: Atom) => Value set: ( atom: WritableAtom, @@ -261,21 +261,12 @@ type PrdStore = { unstable_derive: (fn: (...args: StoreArgs) => StoreArgs) => Store } -type Store = PrdStore | (PrdStore & DevStoreRev4) - export type INTERNAL_DevStoreRev4 = DevStoreRev4 -export type INTERNAL_PrdStore = PrdStore +export type INTERNAL_PrdStore = Store const buildStore = ( ...[getAtomState, atomRead, atomWrite, atomOnMount]: StoreArgs ): Store => { - // for debugging purpose only - let debugMountedAtoms: Set - - if (import.meta.env?.MODE !== 'production') { - debugMountedAtoms = new Set() - } - const setAtomStateValueOrPromise = ( atom: AnyAtom, atomState: AtomState, @@ -617,9 +608,6 @@ const buildStore = ( d: new Set(atomState.d.keys()), t: new Set(), } - if (import.meta.env?.MODE !== 'production') { - debugMountedAtoms.add(atom) - } if (isActuallyWritableAtom(atom)) { const mounted = atomState.m let setAtom: (...args: unknown[]) => unknown @@ -669,9 +657,6 @@ const buildStore = ( addPendingFunction(pending, () => onUnmount(pending)) } delete atomState.m - if (import.meta.env?.MODE !== 'production') { - debugMountedAtoms.delete(atom) - } // unmount dependencies for (const a of atomState.d.keys()) { const aMounted = unmountAtom(pending, a, getAtomState(a)) @@ -706,43 +691,72 @@ const buildStore = ( sub: subscribeAtom, unstable_derive, } - if (import.meta.env?.MODE !== 'production') { - const devStore: DevStoreRev4 = { - // store dev methods (these are tentative and subject to change without notice) - dev4_get_internal_weak_map: () => ({ - get: (atom) => { - const atomState = getAtomState(atom) - if (atomState.n === 0) { - // for backward compatibility - return undefined - } - return atomState - }, - }), - dev4_get_mounted_atoms: () => debugMountedAtoms, - dev4_restore_atoms: (values) => { - const pending = createPending() - for (const [atom, value] of values) { - if (hasInitialValue(atom)) { - const atomState = getAtomState(atom) - const prevEpochNumber = atomState.n - setAtomStateValueOrPromise(atom, atomState, value) - mountDependencies(pending, atom, atomState) - if (prevEpochNumber !== atomState.n) { - addPendingAtom(pending, atom, atomState) - recomputeDependents(pending, atom, atomState) + return store +} + +const deriveDevStore = (store: Store): Store & DevStoreRev4 => { + const debugMountedAtoms = new Set() + let savedGetAtomState: StoreArgs[0] + const derivedStore = store.unstable_derive( + (getAtomState, atomRead, atomWrite, atomOnMount) => { + savedGetAtomState = getAtomState + return [ + getAtomState, + atomRead, + atomWrite, + (atom, setAtom) => { + try { + const onUnmount = atomOnMount(atom, setAtom) + return () => { + debugMountedAtoms.delete(atom) + onUnmount?.() } + } finally { + debugMountedAtoms.add(atom) } + }, + ] + }, + ) + const devStore: DevStoreRev4 = { + // store dev methods (these are tentative and subject to change without notice) + dev4_get_internal_weak_map: () => ({ + get: (atom) => { + const atomState = savedGetAtomState(atom) + if (atomState.n === 0) { + // for backward compatibility + return undefined } - flushPending(pending) + return atomState }, - } - Object.assign(store, devStore) + }), + dev4_get_mounted_atoms: () => debugMountedAtoms, + dev4_restore_atoms: (values) => { + throw new Error('TODO: not implemented yet' || values) + /* + const pending = createPending() + for (const [atom, value] of values) { + if (hasInitialValue(atom)) { + const atomState = savedGetAtomState(atom) + const prevEpochNumber = atomState.n + setAtomStateValueOrPromise(atom, atomState, value) + mountDependencies(pending, atom, atomState) + if (prevEpochNumber !== atomState.n) { + addPendingAtom(pending, atom, atomState) + recomputeDependents(pending, atom, atomState) + } + } + } + flushPending(pending) + */ + }, } - return store + return Object.assign(derivedStore, devStore) } -export const createStore = (): Store => { +type PrdOrDevStore = Store | (Store & DevStoreRev4) + +export const createStore = (): PrdOrDevStore => { const atomStateMap = new WeakMap() const getAtomState = (atom: Atom) => { if (import.meta.env?.MODE !== 'production' && !atom) { @@ -755,17 +769,21 @@ export const createStore = (): Store => { } return atomState } - return buildStore( + const store = buildStore( getAtomState, (atom, ...params) => atom.read(...params), (atom, ...params) => atom.write(...params), (atom, ...params) => atom.onMount?.(...params), ) + if (import.meta.env?.MODE !== 'production') { + return deriveDevStore(store) + } + return store } -let defaultStore: Store | undefined +let defaultStore: PrdOrDevStore | undefined -export const getDefaultStore = (): Store => { +export const getDefaultStore = (): PrdOrDevStore => { if (!defaultStore) { defaultStore = createStore() if (import.meta.env?.MODE !== 'production') { From 4c70c9b6c542764cd0f772834805d6a40420ea2d Mon Sep 17 00:00:00 2001 From: daishi Date: Wed, 11 Dec 2024 20:54:40 +0900 Subject: [PATCH 2/8] hack lint rule --- src/vanilla/store.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 92822267b2..3c1c11ce9e 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -732,7 +732,9 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { }), dev4_get_mounted_atoms: () => debugMountedAtoms, dev4_restore_atoms: (values) => { - throw new Error('TODO: not implemented yet' || values) + throw new Error( + 'TODO: not implemented yet' + ('TODO'.length ? '' : values), + ) /* const pending = createPending() for (const [atom, value] of values) { From 1c51256e62fe1022274a27b4238dd69f3e2a1f01 Mon Sep 17 00:00:00 2001 From: daishi Date: Wed, 11 Dec 2024 21:15:46 +0900 Subject: [PATCH 3/8] fix mounted map --- src/vanilla/store.ts | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 3c1c11ce9e..dda093bc0c 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -695,26 +695,38 @@ const buildStore = ( } const deriveDevStore = (store: Store): Store & DevStoreRev4 => { + const proxyAtomStateMap = new WeakMap() const debugMountedAtoms = new Set() let savedGetAtomState: StoreArgs[0] const derivedStore = store.unstable_derive( (getAtomState, atomRead, atomWrite, atomOnMount) => { savedGetAtomState = getAtomState return [ - getAtomState, - atomRead, - atomWrite, - (atom, setAtom) => { - try { - const onUnmount = atomOnMount(atom, setAtom) - return () => { - debugMountedAtoms.delete(atom) - onUnmount?.() - } - } finally { - debugMountedAtoms.add(atom) + (atom) => { + let proxyAtomState = proxyAtomStateMap.get(atom) + if (!proxyAtomState) { + const atomState = getAtomState(atom) + proxyAtomState = new Proxy(atomState, { + set(target, prop, value) { + if (prop === 'm') { + debugMountedAtoms.add(atom) + } + return Reflect.set(target, prop, value) + }, + deleteProperty(target, prop) { + if (prop === 'm') { + debugMountedAtoms.delete(atom) + } + return Reflect.deleteProperty(target, prop) + }, + }) + proxyAtomStateMap.set(atom, proxyAtomState) } + return proxyAtomState }, + atomRead, + atomWrite, + atomOnMount, ] }, ) From 20e6b3728602f4573c8ff37c9e6610c05dfd05e3 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 19 Dec 2024 10:08:57 +0900 Subject: [PATCH 4/8] implement restore atoms --- src/vanilla/store.ts | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 70f1f3482e..f78748fb3d 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -717,6 +717,7 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { const proxyAtomStateMap = new WeakMap() const debugMountedAtoms = new Set() let savedGetAtomState: StoreArgs[0] + const RESTORE_ATOM = Symbol() const derivedStore = store.unstable_derive( (getAtomState, atomRead, atomWrite, atomOnMount) => { savedGetAtomState = getAtomState @@ -744,7 +745,20 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { return proxyAtomState }, atomRead, - atomWrite, + (atom, getter, setter, ...args) => { + if (RESTORE_ATOM in atom) { + const values = atom[RESTORE_ATOM] as Iterable< + readonly [AnyAtom, AnyValue] + > + for (const [atom, value] of values) { + if (hasInitialValue(atom)) { + setter(atom as never, value) + } + } + return undefined as never + } + return atomWrite(atom, getter, setter, ...args) + }, atomOnMount, ] }, @@ -763,25 +777,13 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { }), dev4_get_mounted_atoms: () => debugMountedAtoms, dev4_restore_atoms: (values) => { - throw new Error( - 'TODO: not implemented yet' + ('TODO'.length ? '' : values), - ) - /* - const pending = createPending() - for (const [atom, value] of values) { - if (hasInitialValue(atom)) { - const atomState = savedGetAtomState(atom) - const prevEpochNumber = atomState.n - setAtomStateValueOrPromise(atom, atomState, value) - mountDependencies(pending, atom, atomState) - if (prevEpochNumber !== atomState.n) { - addPendingAtom(pending, atom, atomState) - recomputeDependents(pending, atom, atomState) - } - } + const restoreAtom: WritableAtom & + Record = { + [RESTORE_ATOM]: values, + read: () => null, + write: () => {}, } - flushPending(pending) - */ + derivedStore.set(restoreAtom) }, } return Object.assign(derivedStore, devStore) From 576a9b042cc517faa12c7135a27ca0cbd43dcf7c Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 19 Dec 2024 11:09:53 +0900 Subject: [PATCH 5/8] save derivedStore.set --- src/vanilla/store.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index f78748fb3d..5fb580963d 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -763,6 +763,7 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { ] }, ) + const savedStoreSet = derivedStore.set const devStore: DevStoreRev4 = { // store dev methods (these are tentative and subject to change without notice) dev4_get_internal_weak_map: () => ({ @@ -783,7 +784,7 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { read: () => null, write: () => {}, } - derivedStore.set(restoreAtom) + savedStoreSet(restoreAtom) }, } return Object.assign(derivedStore, devStore) From 73fcd61d823421e5e8cff2a09d880bc91ac3ed95 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 19 Dec 2024 12:09:08 +0900 Subject: [PATCH 6/8] fix it --- src/vanilla/store.ts | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 5fb580963d..6589d776f9 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -717,7 +717,7 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { const proxyAtomStateMap = new WeakMap() const debugMountedAtoms = new Set() let savedGetAtomState: StoreArgs[0] - const RESTORE_ATOM = Symbol() + let inRestoreAtom = false const derivedStore = store.unstable_derive( (getAtomState, atomRead, atomWrite, atomOnMount) => { savedGetAtomState = getAtomState @@ -746,16 +746,8 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { }, atomRead, (atom, getter, setter, ...args) => { - if (RESTORE_ATOM in atom) { - const values = atom[RESTORE_ATOM] as Iterable< - readonly [AnyAtom, AnyValue] - > - for (const [atom, value] of values) { - if (hasInitialValue(atom)) { - setter(atom as never, value) - } - } - return undefined as never + if (inRestoreAtom) { + return setter(atom, ...args) } return atomWrite(atom, getter, setter, ...args) }, @@ -778,11 +770,20 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { }), dev4_get_mounted_atoms: () => debugMountedAtoms, dev4_restore_atoms: (values) => { - const restoreAtom: WritableAtom & - Record = { - [RESTORE_ATOM]: values, + const restoreAtom: WritableAtom = { read: () => null, - write: () => {}, + write: (_get, set) => { + if (inRestoreAtom) { + throw new Error('[Bug] restoreAtoms cannot be called recursively') + } + inRestoreAtom = true + for (const [atom, value] of values) { + if (hasInitialValue(atom)) { + set(atom as never, value) + } + } + inRestoreAtom = false + }, } savedStoreSet(restoreAtom) }, From 01165212fe81a51d067d9cd7052e91731aa19d48 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 19 Dec 2024 18:05:58 +0900 Subject: [PATCH 7/8] refactor --- src/vanilla/store.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 6589d776f9..376617329f 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -717,7 +717,7 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { const proxyAtomStateMap = new WeakMap() const debugMountedAtoms = new Set() let savedGetAtomState: StoreArgs[0] - let inRestoreAtom = false + let inRestoreAtom = 0 const derivedStore = store.unstable_derive( (getAtomState, atomRead, atomWrite, atomOnMount) => { savedGetAtomState = getAtomState @@ -773,16 +773,16 @@ const deriveDevStore = (store: Store): Store & DevStoreRev4 => { const restoreAtom: WritableAtom = { read: () => null, write: (_get, set) => { - if (inRestoreAtom) { - throw new Error('[Bug] restoreAtoms cannot be called recursively') - } - inRestoreAtom = true - for (const [atom, value] of values) { - if (hasInitialValue(atom)) { - set(atom as never, value) + ++inRestoreAtom + try { + for (const [atom, value] of values) { + if (hasInitialValue(atom)) { + set(atom as never, value) + } } + } finally { + --inRestoreAtom } - inRestoreAtom = false }, } savedStoreSet(restoreAtom) From 8aa1d880fdea126e2ab614c3a4692d7836dac7bb Mon Sep 17 00:00:00 2001 From: daishi Date: Fri, 20 Dec 2024 14:59:20 +0900 Subject: [PATCH 8/8] rename --- src/vanilla/store.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 70d39a90df..395ff345c2 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -711,7 +711,7 @@ const buildStore = ( return store } -const deriveDevStore = (store: Store): Store & DevStoreRev4 => { +const deriveDevStoreRev4 = (store: Store): Store & DevStoreRev4 => { const proxyAtomStateMap = new WeakMap() const debugMountedAtoms = new Set() let savedGetAtomState: StoreArgs[0] @@ -811,7 +811,7 @@ export const createStore = (): PrdOrDevStore => { (atom, ...params) => atom.onMount?.(...params), ) if (import.meta.env?.MODE !== 'production') { - return deriveDevStore(store) + return deriveDevStoreRev4(store) } return store }