diff options
author | Vendicated <vendicated@riseup.net> | 2023-05-06 01:36:00 +0200 |
---|---|---|
committer | Vendicated <vendicated@riseup.net> | 2023-05-06 01:36:00 +0200 |
commit | 0d5e2d0696da494aee2126b4cadbca7e07066b89 (patch) | |
tree | 4a8159ba43f5f283e28101eb3d92e1f4f0b52035 /src/utils/lazy.ts | |
parent | 2834bed518a1fc1c384a93d599cc1b03555177c7 (diff) | |
download | Vencord-0d5e2d0696da494aee2126b4cadbca7e07066b89.tar.gz Vencord-0d5e2d0696da494aee2126b4cadbca7e07066b89.tar.bz2 Vencord-0d5e2d0696da494aee2126b4cadbca7e07066b89.zip |
[skip ci] Refactor utils
Diffstat (limited to 'src/utils/lazy.ts')
-rw-r--r-- | src/utils/lazy.ts | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/src/utils/lazy.ts b/src/utils/lazy.ts new file mode 100644 index 0000000..1e1dd5f --- /dev/null +++ b/src/utils/lazy.ts @@ -0,0 +1,87 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +export function makeLazy<T>(factory: () => T): () => T { + let cache: T; + return () => cache ?? (cache = factory()); +} + +// Proxies demand that these properties be unmodified, so proxyLazy +// will always return the function default for them. +const unconfigurable = ["arguments", "caller", "prototype"]; + +const handler: ProxyHandler<any> = {}; + +const GET_KEY = Symbol.for("vencord.lazy.get"); +const CACHED_KEY = Symbol.for("vencord.lazy.cached"); + +for (const method of [ + "apply", + "construct", + "defineProperty", + "deleteProperty", + "get", + "getOwnPropertyDescriptor", + "getPrototypeOf", + "has", + "isExtensible", + "ownKeys", + "preventExtensions", + "set", + "setPrototypeOf" +]) { + handler[method] = + (target: any, ...args: any[]) => Reflect[method](target[GET_KEY](), ...args); +} + +handler.ownKeys = target => { + const v = target[GET_KEY](); + const keys = Reflect.ownKeys(v); + for (const key of unconfigurable) { + if (!keys.includes(key)) keys.push(key); + } + return keys; +}; + +handler.getOwnPropertyDescriptor = (target, p) => { + if (typeof p === "string" && unconfigurable.includes(p)) + return Reflect.getOwnPropertyDescriptor(target, p); + + const descriptor = Reflect.getOwnPropertyDescriptor(target[GET_KEY](), p); + + if (descriptor) Object.defineProperty(target, p, descriptor); + return descriptor; +}; + +/** + * Wraps the result of {@see makeLazy} in a Proxy you can consume as if it wasn't lazy. + * On first property access, the lazy is evaluated + * @param factory lazy factory + * @returns Proxy + * + * Note that the example below exists already as an api, see {@link findByPropsLazy} + * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); + */ +export function proxyLazy<T>(factory: () => T): T { + const proxyDummy: { (): void;[CACHED_KEY]?: T;[GET_KEY](): T; } = Object.assign(function () { }, { + [CACHED_KEY]: void 0, + [GET_KEY]: () => proxyDummy[CACHED_KEY] ??= factory(), + }); + + return new Proxy(proxyDummy, handler) as any; +} |