refactor: lazification

This commit is contained in:
amsyarasyiq 2024-07-09 17:19:29 +08:00
parent 7e3c8b81d2
commit 035ee80045
5 changed files with 85 additions and 35 deletions

View file

@ -1,10 +1,10 @@
import { getFactoryOfProxy } from "@lib/utils/lazy";
import { getProxyFactory } from "@lib/utils/lazy";
import { findByPropsProxy } from "@metro/utils";
const jsxRuntime = findByPropsProxy("jsx", "jsxs", "Fragment");
function unproxyFirstArg<T>(args: T[]) {
const factory = getFactoryOfProxy(args[0]);
const factory = getProxyFactory(args[0]);
if (factory) args[0] = factory();
return args;
}

View file

@ -9,7 +9,7 @@ import * as plugins from "@lib/managers/plugins";
import * as themes from "@lib/managers/themes";
import { loaderConfig, settings } from "@lib/settings";
import * as utils from "@lib/utils";
import { logModule } from "@lib/utils/logger";
import { DiscordLogger } from "@lib/utils/logger";
import * as metro from "@metro";
import * as common from "@metro/common";
import { Forms } from "@metro/common/components";
@ -32,7 +32,7 @@ export async function createVdPluginObject(plugin: plugins.BunnyPlugin) {
// Wrapping this with wrapSync is NOT an option.
storage: await createStorage<Record<string, any>>(storage.createMMKVBackend(plugin.id)),
},
logger: new logModule(`Bunny » ${plugin.manifest.name}`),
logger: new DiscordLogger(`Bunny » ${plugin.manifest.name}`),
};
}

View file

@ -1,34 +1,76 @@
/* eslint-disable func-call-spacing */
const unconfigurable = ["arguments", "caller", "prototype"];
const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key);
const proxyToFactoryMap = new WeakMap<any, () => any>();
const dummyToFactoryMap = new WeakMap<any, () => any>();
interface IsolatedEntries {
[keys: symbol | string]: any;
}
interface LazyOptions {
hint?: "function" | "object";
isolatedEntries?: IsolatedEntries
}
interface ContextHolder {
options: LazyOptions;
factory: any
}
const unconfigurable = new Set(["arguments", "caller", "prototype"]);
const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.has(key);
const factories = new WeakMap<any, () => any>();
const proxyContextHolder = new WeakMap<any, ContextHolder>();
const lazyHandler: ProxyHandler<any> = {
...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(fnName => {
return [fnName, (target: any, ...args: any[]) => {
const resolved = dummyToFactoryMap.get(target)!();
if (!resolved) throw new Error(`Trying to ${fnName} of ${typeof resolved}`);
const contextHolder = proxyContextHolder.get(target);
const resolved = contextHolder?.factory();
if (!resolved) throw new Error(`Trying to Reflect.${fnName} of ${typeof resolved}`);
// @ts-expect-error
return Reflect[fnName](resolved, ...args);
}];
})),
ownKeys: target => {
const resolved = dummyToFactoryMap.get(target)!();
if (!resolved) throw new Error(`Trying to ownKeys of ${typeof resolved}`);
has(target, p) {
const contextHolder = proxyContextHolder.get(target);
const cacheKeys = Reflect.ownKeys(dummyToFactoryMap.get(target)!());
if (contextHolder?.options) {
const { isolatedEntries } = contextHolder.options;
if (isolatedEntries && p in isolatedEntries) return true;
}
const resolved = contextHolder?.factory();
if (!resolved) throw new Error(`Trying to Reflect.has of ${typeof resolved}`);
return Reflect.has(resolved, p);
},
get(target, p, receiver) {
const contextHolder = proxyContextHolder.get(target);
if (contextHolder?.options) {
const { isolatedEntries } = contextHolder.options;
if (isolatedEntries?.[p]) return isolatedEntries[p];
}
const resolved = contextHolder?.factory();
if (!resolved) throw new Error(`Trying to Reflect.get of ${typeof resolved}`);
return Reflect.get(resolved, p, receiver);
},
ownKeys: target => {
const contextHolder = proxyContextHolder.get(target);
const resolved = contextHolder?.factory();
if (!resolved) throw new Error(`Trying to Reflect.ownKeys of ${typeof resolved}`);
const cacheKeys = Reflect.ownKeys(resolved);
unconfigurable.forEach(key => !cacheKeys.includes(key) && cacheKeys.push(key));
return cacheKeys;
},
getOwnPropertyDescriptor: (target, p) => {
const resolved = dummyToFactoryMap.get(target)!();
const contextHolder = proxyContextHolder.get(target);
const resolved = contextHolder?.factory();
if (!resolved) throw new Error(`Trying to getOwnPropertyDescriptor of ${typeof resolved}`);
if (isUnconfigurable(p)) return Reflect.getOwnPropertyDescriptor(target, p);
const descriptor = Reflect.getOwnPropertyDescriptor(dummyToFactoryMap.get(target)!(), p);
const descriptor = Reflect.getOwnPropertyDescriptor(resolved, p);
if (descriptor) Object.defineProperty(target, p, descriptor);
return descriptor;
},
@ -41,15 +83,18 @@ const lazyHandler: ProxyHandler<any> = {
* @returns A proxy that will call the factory function only when needed
* @example const ChannelStore = proxyLazy(() => findByProps("getChannelId"));
*/
export function proxyLazy<T>(factory: () => T, asFunction = true): T {
export function proxyLazy<T>(factory: () => T, opts: LazyOptions = {}): T {
let cache: T;
const dummy = asFunction ? function () { } as any : {};
const dummy = opts.hint !== "object" ? function () { } as any : {};
const proxyFactory = () => cache ??= factory();
const proxy = new Proxy(dummy, lazyHandler) as T;
proxyToFactoryMap.set(proxy, proxyFactory);
dummyToFactoryMap.set(dummy, proxyFactory);
factories.set(proxy, proxyFactory);
proxyContextHolder.set(dummy, {
factory,
options: opts,
});
return proxy;
}
@ -63,8 +108,8 @@ export function proxyLazy<T>(factory: () => T, asFunction = true): T {
* const { uuid4 } = lazyDestructure(() => findByProps("uuid4"))
* uuid4; // <- is a lazy proxy!
*/
export function lazyDestructure<T extends Record<PropertyKey, unknown>>(factory: () => T, asFunction = false): T {
const proxiedObject = proxyLazy(factory, asFunction);
export function lazyDestructure<T extends Record<PropertyKey, unknown>>(factory: () => T, opts: LazyOptions = {}): T {
const proxiedObject = proxyLazy(factory);
return new Proxy({}, {
get(_, property) {
@ -72,16 +117,16 @@ export function lazyDestructure<T extends Record<PropertyKey, unknown>>(factory:
return function* () {
yield proxiedObject;
yield new Proxy({}, {
get: (_, p) => proxyLazy(() => proxiedObject[p])
get: (_, p) => proxyLazy(() => proxiedObject[p], opts)
});
throw new Error("This is not a real iterator, this is likely used incorrectly");
};
}
return proxyLazy(() => proxiedObject[property]);
return proxyLazy(() => proxiedObject[property], opts);
}
}) as T;
}
export function getFactoryOfProxy<T>(obj: T): (() => T) | void {
return proxyToFactoryMap.get(obj) as (() => T) | void;
export function getProxyFactory<T>(obj: T): (() => T) | void {
return factories.get(obj) as (() => T) | void;
}

View file

@ -11,5 +11,5 @@ export interface Logger {
verbose: LoggerFunction;
}
export const logModule = findByNameProxy("Logger");
export const logger: Logger = new logModule("Bunny");
export const DiscordLogger = findByNameProxy("Logger");
export const logger: Logger = new DiscordLogger("Bunny");

View file

@ -5,9 +5,10 @@ import { findExports } from "./finders";
import { metroModules, subscribeModule } from "./modules";
import type { FilterFn } from "./types";
const proxyContextMap = new WeakMap<any, FindProxyContext<any[]>>();
const moduleContextSymbol = Symbol.for("bunny.metro.modules");
const proxyContexts = new WeakMap<any, ModuleProxyContext<any[]>>();
interface FindProxyContext<A extends unknown[]> {
interface ModuleProxyContext<A extends unknown[]> {
filter: FilterFn<A>;
indexed: boolean;
moduleId: number | undefined;
@ -34,15 +35,15 @@ function subscribeModuleOfFind(proxy: any, callback: (exports: any) => void) {
});
}
export function getFindContext<A extends unknown[]>(proxy: any): FindProxyContext<A> | void {
return proxyContextMap.get(proxy) as unknown as FindProxyContext<A>;
export function getFindContext<A extends unknown[]>(proxy: any): ModuleProxyContext<A> | void {
return proxyContexts.get(proxy) as unknown as ModuleProxyContext<A>;
}
export function createFindProxy<A extends unknown[]>(filter: FilterFn<A>) {
let cache: any = undefined;
const moduleId = getIndexedSingleFind(filter);
const context: FindProxyContext<A> = {
const context: ModuleProxyContext<A> = {
filter,
indexed: !!moduleId,
moduleId,
@ -65,8 +66,12 @@ export function createFindProxy<A extends unknown[]>(filter: FilterFn<A>) {
}
};
const proxy = proxyLazy(() => context.unproxy());
proxyContextMap.set(proxy, context as FindProxyContext<any>);
const proxy = proxyLazy(() => context.unproxy(), {
isolatedEntries: {
[moduleContextSymbol]: context
}
});
proxyContexts.set(proxy, context as ModuleProxyContext<any>);
return proxy;
}