Last active
January 11, 2022 12:57
-
-
Save LokiMidgard/24eb71fc8717923938970e22a4113452 to your computer and use it in GitHub Desktop.
Flat Store for svelte
This file contains 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 { validate_each_argument } from "svelte/internal"; | |
import { get, readable, Readable, Writable, writable } from "svelte/store"; | |
export type NoStore<T> = T extends Readable<infer T2> | |
? NoStoreParameter<T2> | |
: NoStoreParameter<T>; | |
type ExtractType<TProperty, TType, TExtends> = TType extends TExtends ? TProperty : never; | |
type ExcludeType<TProperty, TType, TExtends> = TType extends TExtends ? never : TProperty; | |
type NoStoreParameter<T> = | |
{ | |
-readonly [Property in keyof T as ExtractType<Property, T[Property], Writable<any>>]: T[Property] extends Writable<infer Args> | |
? NoStore<Args> | |
: never // should not happen | |
} | |
& | |
{ | |
readonly [Property in keyof T as ExcludeType<Property, T[Property], Writable<any>>]: T[Property] extends Function | |
? T[Property] | |
: T[Property] extends Readable<infer Args> | |
? NoStore<Args> | |
: NoStore<T[Property]> | |
} | |
export function flatStore<T>(source: T): Readable<NoStore<T>> { | |
return readable({} as NoStore<T>, function start(set) { | |
let destroyCallback: (() => void)[] = []; | |
const updated: () => void = () => { | |
destroyCallback.forEach(x => x()); | |
const newDestroyCallback: (() => void)[] = []; | |
const newValue = mapStoreInternal(source, { update: updated, onDestroy: newDestroyCallback }) | |
destroyCallback = newDestroyCallback; | |
set(newValue) | |
}; | |
const startValue = mapStoreInternal(source, { update: updated, onDestroy: destroyCallback }) | |
set(startValue); | |
return function stop() { | |
destroyCallback.forEach(x => x()); | |
} | |
}); | |
} | |
function mapStoreInternal<T>(source: T, callbacks?: { update: () => void, onDestroy: (() => void)[] }): NoStore<T> { | |
if (isStore(source)) { | |
const value = get(source); | |
if (callbacks) { | |
const unsubscribe = source.subscribe(x => { | |
if (value !== x) { | |
callbacks.update(); | |
} | |
}) | |
callbacks.onDestroy.push(unsubscribe); | |
} | |
return mapStoreInternal(value, callbacks) as NoStore<T>; | |
} else if (Array.isArray(source)) { | |
const result: any[] = [] | |
for (let index = 0; index < source.length; index++) { | |
const element = source[index]; | |
result.push(mapStoreInternal(element, callbacks)); | |
} | |
return result as any; | |
} else if (typeof source === "object") { | |
const result: any = {} | |
for (const key in source) { | |
if (Object.prototype.hasOwnProperty.call(source, key)) { | |
const element = source[key]; | |
const newValue = mapStoreInternal(element, callbacks); | |
if (isWritableStore(element)) { | |
Object.defineProperty(result, key, { | |
get: function () { | |
return newValue; | |
}, | |
set: function (v: any) { | |
element.set(v); | |
} | |
}); | |
} else { | |
Object.defineProperty(result, key, { | |
get: function () { | |
return newValue; | |
} | |
}); | |
} | |
} | |
} | |
for (const f of getAllFuncs(source)) { | |
result[f] = ((...args: any) => source[f](...args)) as any; | |
} | |
return result; | |
} | |
else { | |
// only stuff like string and bigint | |
return source as any; | |
} | |
} | |
function getAllFuncs(toCheck) { | |
const props: string[] = []; | |
let obj = toCheck; | |
do { | |
props.push(...Object.getOwnPropertyNames(obj)); | |
} while (obj = Object.getPrototypeOf(obj)); | |
return props.sort().filter((e, i, arr) => { | |
if (e != arr[i + 1] && typeof toCheck[e] == 'function') return true; | |
}); | |
} | |
function isStore(value: any): value is Readable<any> { | |
if (value) | |
return typeof value.subscribe == "function"; | |
return false; | |
} | |
function isWritableStore(value: any): value is Writable<any> { | |
if (value) | |
return typeof value.set == "function" && typeof value.update == "function"; | |
return false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Added support for funcitons