fix(metro): magical fixes

This commit is contained in:
amsyarasyiq 2024-05-21 16:04:56 +08:00
parent ee6669f514
commit af6ba01079
4 changed files with 41 additions and 32 deletions

View file

@ -1,40 +1,45 @@
const factorySymbol = Symbol.for("bunny.lazyFactory"); const factorySymbol = Symbol("lazyFactory");
const cacheSymbol = Symbol("bunny.lazyCache"); const cacheSymbol = Symbol("lazyCache");
const dummySymbol = Symbol("bunny.lazyDummy");
const unconfigurable = ["arguments", "caller", "prototype"]; const unconfigurable = ["arguments", "caller", "prototype"];
const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key); const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key);
const lazyHandler: ProxyHandler<any> = { const lazyHandler: ProxyHandler<any> = {
...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 => { ownKeys: target => {
const cacheKeys = Reflect.ownKeys(target[factorySymbol]() ?? target[dummySymbol]); const cacheKeys = Reflect.ownKeys(target[factorySymbol]());
unconfigurable.forEach(key => isUnconfigurable(key) && cacheKeys.push(key)); unconfigurable.forEach(key => isUnconfigurable(key) && cacheKeys.push(key));
return cacheKeys; return cacheKeys;
}, },
getOwnPropertyDescriptor: (target, p) => { getOwnPropertyDescriptor: (target, p) => {
if (isUnconfigurable(p)) return Reflect.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); if (descriptor) Object.defineProperty(target, p, descriptor);
return 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) * 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 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 * @returns A proxy that will call the factory function only when needed
* @example const ChannelStore = proxyLazy(() => findByProps("getChannelId"));
*/ */
export function proxyLazy<T>(factory: () => T, dummy: any = () => void 0): T { export function proxyLazy<T>(factory: () => T, fallback?: any): T {
dummy[factorySymbol] = () => dummy[cacheSymbol] ??= factory(); const dummy = function () { } as any;
dummy[dummySymbol] = dummy; dummy[factorySymbol] = () => {
return (dummy[cacheSymbol] ??= factory()) ?? fallback;
};
return new Proxy(dummy, lazyHandler) as any; return new Proxy(dummy, lazyHandler) as any;
} }

View file

@ -1,8 +1,6 @@
import { ClientInfoManager, MMKVManager } from "@lib/api/native/modules"; import { ClientInfoManager, MMKVManager } from "@lib/api/native/modules";
import { throttle } from "@lib/utils/throttle"; import { throttle } from "@lib/utils/throttle";
import { getCachedPolyfillModules } from "./modules";
type ModulesMap = { type ModulesMap = {
_?: 1; _?: 1;
[id: number]: 1 | void; [id: number]: 1 | void;
@ -22,6 +20,7 @@ let _metroCache = null as unknown as MetroCacheStore;
export function getMetroCache() { export function getMetroCache() {
return _metroCache; return _metroCache;
} }
window.getMetroCache = getMetroCache;
function buildInitCache() { function buildInitCache() {
_metroCache = { _metroCache = {
@ -59,14 +58,16 @@ export async function initMetroCache() {
const saveCache = throttle(() => MMKVManager.setItem(BUNNY_METRO_CACHE_KEY, JSON.stringify(_metroCache))); const saveCache = throttle(() => MMKVManager.setItem(BUNNY_METRO_CACHE_KEY, JSON.stringify(_metroCache)));
export function getCacherForUniq(uniq: string, allFind: boolean) { export function getCacherForUniq(uniq: string, allFind: boolean) {
const indexObject = _metroCache.findIndex[uniq] ??= {}; let indexObject = _metroCache.findIndex[uniq];
return { return {
cacheId(moduleId: number) { cacheId(moduleId: number) {
indexObject = _metroCache.findIndex[uniq] ??= {};
indexObject[moduleId] = 1; indexObject[moduleId] = 1;
saveCache(); saveCache();
}, },
finish() { finish() {
indexObject = _metroCache.findIndex[uniq] ??= {};
if (allFind) indexObject._ = 1; if (allFind) indexObject._ = 1;
saveCache(); saveCache();
} }
@ -74,13 +75,14 @@ export function getCacherForUniq(uniq: string, allFind: boolean) {
} }
export function getPolyfillModuleCacher(name: string) { export function getPolyfillModuleCacher(name: string) {
const indexObject = _metroCache.polyfillIndex[name] ??= {}; let indexObject = _metroCache.polyfillIndex[name];
return { return {
getModules() { getModules() {
return getCachedPolyfillModules(name); return require("@metro/modules").getCachedPolyfillModules(name);
}, },
cacheId(moduleId: number) { cacheId(moduleId: number) {
indexObject = _metroCache.polyfillIndex[name] ??= {};
indexObject[moduleId] = 1; indexObject[moduleId] = 1;
saveCache(); saveCache();
} }

View file

@ -4,7 +4,7 @@ import { FilterFn } from "./types";
function testExports<A extends unknown[]>(moduleExports: any, moduleId: number, filter: FilterFn<A>) { function testExports<A extends unknown[]>(moduleExports: any, moduleId: number, filter: FilterFn<A>) {
if (moduleExports.default && moduleExports.__esModule && filter(moduleExports.default, moduleId, true)) { 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)) { if (!filter.defaultFilter && filter(moduleExports, moduleId, false)) {

View file

@ -13,16 +13,6 @@ let patchedInspectSource = false;
let patchedImportTracker = false; let patchedImportTracker = false;
let _importingModuleId: string | null = null; 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["<!@ pylix was here :fuyusquish: !@>"] === null;
}
for (const id in metroModules) { for (const id in metroModules) {
const metroModule = metroModules[id]; 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["<!@ pylix was here :fuyusquish: !@>"] === null;
}
function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { function onModuleRequire(moduleExports: any, id: Metro.ModuleID) {
// Temporary // Temporary
moduleExports.initSentry &&= () => undefined; moduleExports.initSentry &&= () => undefined;
@ -136,6 +136,8 @@ export function requireModule(id: Metro.ModuleID) {
if (!metroModules[0]?.isInitialized) metroRequire(0); if (!metroModules[0]?.isInitialized) metroRequire(0);
if (blacklistedIds.has(String(id))) return undefined; if (blacklistedIds.has(String(id))) return undefined;
if (Number(id) === -1) return require("@metro/polyfills/redesign");
if (metroModules[id]?.isInitialized && !metroModules[id]?.hasError) { if (metroModules[id]?.isInitialized && !metroModules[id]?.hasError) {
return metroRequire(id); return metroRequire(id);
} }