Created
December 15, 2023 05:34
-
-
Save oscartbeaumont/1d78018d4425684fecbd541c7c3e180b to your computer and use it in GitHub Desktop.
Solid good good good
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ParentProps, createContext, useContext } from "solid-js"; | |
import { SetStoreFunction, Store, createStore } from "solid-js/store"; | |
export type CacheNode = { | |
__type: string; | |
__id: string; | |
} & Record<string, unknown>; | |
const ctx = createContext<Cache>(); | |
export type UseCacheResult<T> = T extends (infer A)[] | |
? UseCacheResult<A>[] | |
: T extends object | |
? T extends { __type: any; __id: string; "#type": infer U } | |
? UseCacheResult<U> | |
: { [K in keyof T]: UseCacheResult<T[K]> } | |
: { [K in keyof T]: UseCacheResult<T[K]> }; | |
function constructCache( | |
nodes: Store<Nodes>, | |
setNodes: SetStoreFunction<Nodes> | |
) { | |
return { | |
getNodes: () => nodes, | |
getNode: (type: string, id: string) => | |
nodes?.[type]?.[id] as unknown | undefined, | |
setNodes: (newNodes: CacheNode | CacheNode[]) => { | |
if (!Array.isArray(newNodes)) newNodes = [newNodes]; | |
for (const node of newNodes) { | |
if (!(typeof node === "object" || "__type" in node || "__id" in node)) | |
throw new Error( | |
`Tried to 'setNodes' but encountered invalid node '${JSON.stringify( | |
node | |
)}'` | |
); | |
const { __type, __id, ...copy } = { ...node } as any; | |
if (!nodes[node.__type]) setNodes(node.__type, {}); | |
setNodes(node.__type, node.__id, copy); // Be aware this is a merge, not a replace | |
} | |
}, | |
useCache<T>(item: T) { | |
return restore(nodes, item) as UseCacheResult<T>; | |
}, | |
}; | |
} | |
export type Cache = ReturnType<typeof constructCache>; | |
export type Nodes = Record<string, Record<string, unknown>>; | |
export function createCache() { | |
const [nodes, setNodes] = createStore({} as Nodes); | |
const cache = constructCache(nodes, setNodes); | |
return { | |
...cache, | |
Provider: (props: ParentProps) => ( | |
<ctx.Provider value={cache}>{props.children}</ctx.Provider> | |
), | |
}; | |
} | |
export function useCache() { | |
const c = useContext(ctx); | |
if (!c) throw new Error("Did you forget to mount `cache.Provider`?"); | |
return c; | |
} | |
function restore(nodes: Store<Nodes>, item: unknown): unknown { | |
if (item === undefined || item === null) { | |
return item; | |
} else if (Array.isArray(item)) { | |
return item.map((v) => restore(nodes, v)); | |
} else if (typeof item === "object") { | |
if ("__type" in item && "__id" in item) { | |
if (typeof item.__type !== "string") throw new Error("Invalid `__type`"); | |
if (typeof item.__id !== "string") throw new Error("Invalid `__id`"); | |
const result = nodes?.[item.__type]?.[item.__id]; | |
if (!result) | |
throw new Error( | |
`Missing node for id '${item.__id}' of type '${item.__type}'` | |
); | |
return result; | |
} | |
return Object.fromEntries( | |
Object.entries(item).map(([key, value]) => [key, restore(nodes, value)]) | |
); | |
} | |
return item; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment