diff --git a/src/lib/utils/lazy.ts b/src/lib/utils/lazy.ts index 19330cd..4f76b76 100644 --- a/src/lib/utils/lazy.ts +++ b/src/lib/utils/lazy.ts @@ -1,40 +1,45 @@ -const factorySymbol = Symbol.for("bunny.lazyFactory"); -const cacheSymbol = Symbol("bunny.lazyCache"); -const dummySymbol = Symbol("bunny.lazyDummy"); +const factorySymbol = Symbol("lazyFactory"); +const cacheSymbol = Symbol("lazyCache"); const unconfigurable = ["arguments", "caller", "prototype"]; const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key); const lazyHandler: ProxyHandler = { - ...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(fnName => { - return [fnName, (target: any, ...args: any[]) => { - // @ts-ignore - const func = s => Reflect[fnName](s, ...args); - return func(target[factorySymbol]()) ?? func(target[dummySymbol]); - }]; - })), ownKeys: target => { - const cacheKeys = Reflect.ownKeys(target[factorySymbol]() ?? target[dummySymbol]); + const cacheKeys = Reflect.ownKeys(target[factorySymbol]()); unconfigurable.forEach(key => isUnconfigurable(key) && cacheKeys.push(key)); return cacheKeys; }, getOwnPropertyDescriptor: (target, p) => { if (isUnconfigurable(p)) return Reflect.getOwnPropertyDescriptor(target, p); - const descriptor = Reflect.getOwnPropertyDescriptor(target[factorySymbol]() ?? target[dummySymbol], p); + const descriptor = Reflect.getOwnPropertyDescriptor(target[factorySymbol](), p); if (descriptor) Object.defineProperty(target, p, descriptor); return descriptor; - }, + } }; +// Mirror all Reflect methods +Object.getOwnPropertyNames(Reflect).forEach(fnName => { + // @ts-ignore + lazyHandler[fnName] ??= (target: any, ...args: any[]) => { + // @ts-ignore + return Reflect[fnName](target[factorySymbol](), ...args); + }; +}); + /** * Lazy proxy that will only call the factory function when needed (when a property is accessed) * @param factory Factory function to create the object + * @param fallback A fallback value to return if the factory returns undefined * @returns A proxy that will call the factory function only when needed + * @example const ChannelStore = proxyLazy(() => findByProps("getChannelId")); */ -export function proxyLazy(factory: () => T, dummy: any = () => void 0): T { - dummy[factorySymbol] = () => dummy[cacheSymbol] ??= factory(); - dummy[dummySymbol] = dummy; +export function proxyLazy(factory: () => T, fallback?: any): T { + const dummy = function () { } as any; + dummy[factorySymbol] = () => { + return (dummy[cacheSymbol] ??= factory()) ?? fallback; + }; return new Proxy(dummy, lazyHandler) as any; } diff --git a/src/metro/caches.ts b/src/metro/caches.ts index 62d69cb..3bd77f6 100644 --- a/src/metro/caches.ts +++ b/src/metro/caches.ts @@ -1,8 +1,6 @@ import { ClientInfoManager, MMKVManager } from "@lib/api/native/modules"; import { throttle } from "@lib/utils/throttle"; -import { getCachedPolyfillModules } from "./modules"; - type ModulesMap = { _?: 1; [id: number]: 1 | void; @@ -22,6 +20,7 @@ let _metroCache = null as unknown as MetroCacheStore; export function getMetroCache() { return _metroCache; } +window.getMetroCache = getMetroCache; function buildInitCache() { _metroCache = { @@ -59,14 +58,16 @@ export async function initMetroCache() { const saveCache = throttle(() => MMKVManager.setItem(BUNNY_METRO_CACHE_KEY, JSON.stringify(_metroCache))); export function getCacherForUniq(uniq: string, allFind: boolean) { - const indexObject = _metroCache.findIndex[uniq] ??= {}; + let indexObject = _metroCache.findIndex[uniq]; return { cacheId(moduleId: number) { + indexObject = _metroCache.findIndex[uniq] ??= {}; indexObject[moduleId] = 1; saveCache(); }, finish() { + indexObject = _metroCache.findIndex[uniq] ??= {}; if (allFind) indexObject._ = 1; saveCache(); } @@ -74,13 +75,14 @@ export function getCacherForUniq(uniq: string, allFind: boolean) { } export function getPolyfillModuleCacher(name: string) { - const indexObject = _metroCache.polyfillIndex[name] ??= {}; + let indexObject = _metroCache.polyfillIndex[name]; return { getModules() { - return getCachedPolyfillModules(name); + return require("@metro/modules").getCachedPolyfillModules(name); }, cacheId(moduleId: number) { + indexObject = _metroCache.polyfillIndex[name] ??= {}; indexObject[moduleId] = 1; saveCache(); } diff --git a/src/metro/finders.ts b/src/metro/finders.ts index 5db2695..bd447de 100644 --- a/src/metro/finders.ts +++ b/src/metro/finders.ts @@ -4,7 +4,7 @@ import { FilterFn } from "./types"; function testExports(moduleExports: any, moduleId: number, filter: FilterFn) { if (moduleExports.default && moduleExports.__esModule && filter(moduleExports.default, moduleId, true)) { - return [moduleExports.default, true]; + return [filter.defaultFilter ? moduleExports : moduleExports.default, !filter.defaultFilter]; } if (!filter.defaultFilter && filter(moduleExports, moduleId, false)) { diff --git a/src/metro/modules.ts b/src/metro/modules.ts index 3f8508d..d4e57d6 100644 --- a/src/metro/modules.ts +++ b/src/metro/modules.ts @@ -13,16 +13,6 @@ let patchedInspectSource = false; let patchedImportTracker = false; let _importingModuleId: string | null = null; -/** Makes the module associated with the specified ID non-enumberable. */ -function blacklistModule(id: string) { - Object.defineProperty(metroModules, id, { enumerable: false }); - blacklistedIds.add(id); -} - -function isBadExports(exports: any) { - return !exports || exports === window || exports[""] === null; -} - for (const id in metroModules) { const metroModule = metroModules[id]; @@ -57,6 +47,16 @@ for (const id in metroModules) { } } +/** Makes the module associated with the specified ID non-enumberable. */ +function blacklistModule(id: string) { + Object.defineProperty(metroModules, id, { enumerable: false }); + blacklistedIds.add(id); +} + +function isBadExports(exports: any) { + return !exports || exports === window || exports[""] === null; +} + function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { // Temporary moduleExports.initSentry &&= () => undefined; @@ -136,6 +136,8 @@ export function requireModule(id: Metro.ModuleID) { if (!metroModules[0]?.isInitialized) metroRequire(0); if (blacklistedIds.has(String(id))) return undefined; + if (Number(id) === -1) return require("@metro/polyfills/redesign"); + if (metroModules[id]?.isInitialized && !metroModules[id]?.hasError) { return metroRequire(id); }