feat+refactor(metro&ui/proxy): print more detailed errors
This commit is contained in:
parent
28b02ae60a
commit
abf89e3695
5 changed files with 53 additions and 39 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { getFindProxyInfo } from "@metro/proxy";
|
||||
import { getFindContext } from "@metro/proxy";
|
||||
import {
|
||||
after as _after,
|
||||
before as _before,
|
||||
|
|
@ -10,7 +10,7 @@ type ProxyPatchParameters<T, P> = [func: string, parent: [P, (t: P) => any], ...
|
|||
|
||||
function shim<T extends (func: string, parent: any, ...args: ParametersAfterOneArgs<T>) => any>(fn: T) {
|
||||
function shimmed(this: any, ...args: Parameters<T>) {
|
||||
const proxyInfo = getFindProxyInfo(args[1]);
|
||||
const proxyInfo = getFindContext(args[1]);
|
||||
if (proxyInfo && !proxyInfo.cache && proxyInfo.indexed) {
|
||||
let cancel = false;
|
||||
let unpatch = () => cancel = true;
|
||||
|
|
@ -27,7 +27,7 @@ function shim<T extends (func: string, parent: any, ...args: ParametersAfterOneA
|
|||
|
||||
shimmed.proxy = function proxyPatch<P>(this: any, ...args: ProxyPatchParameters<T, P>) {
|
||||
const [target, resolve] = args[1];
|
||||
const proxyInfo = getFindProxyInfo(target);
|
||||
const proxyInfo = getFindContext(target);
|
||||
if (!proxyInfo) {
|
||||
args[1] = resolve(target);
|
||||
return shimmed(...args as unknown as Parameters<T>);
|
||||
|
|
|
|||
|
|
@ -1,54 +1,68 @@
|
|||
const factorySymbol = Symbol.for("bunny.lazy.factory");
|
||||
const cacheSymbol = Symbol.for("bunny.lazy.cache");
|
||||
|
||||
/* eslint-disable func-call-spacing */
|
||||
const unconfigurable = ["arguments", "caller", "prototype"];
|
||||
const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key);
|
||||
|
||||
// eslint-disable-next-line func-call-spacing
|
||||
const proxyToFactoryMap = new WeakMap<any, () => any>();
|
||||
const dummyToFactoryMap = new WeakMap<any, () => any>();
|
||||
|
||||
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}`);
|
||||
// @ts-expect-error
|
||||
return Reflect[fnName](resolved, ...args);
|
||||
}];
|
||||
})),
|
||||
ownKeys: target => {
|
||||
const cacheKeys = Reflect.ownKeys(target[factorySymbol]());
|
||||
unconfigurable.forEach(key => isUnconfigurable(key) && cacheKeys.push(key));
|
||||
const resolved = dummyToFactoryMap.get(target)!();
|
||||
if (!resolved) throw new Error(`Trying to ownKeys of ${typeof resolved}`);
|
||||
|
||||
const cacheKeys = Reflect.ownKeys(dummyToFactoryMap.get(target)!());
|
||||
unconfigurable.forEach(key => !cacheKeys.includes(key) && cacheKeys.push(key));
|
||||
return cacheKeys;
|
||||
},
|
||||
getOwnPropertyDescriptor: (target, p) => {
|
||||
const resolved = dummyToFactoryMap.get(target)!();
|
||||
if (!resolved) throw new Error(`Trying to getOwnPropertyDescriptor of ${typeof resolved}`);
|
||||
|
||||
if (isUnconfigurable(p)) return Reflect.getOwnPropertyDescriptor(target, p);
|
||||
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(target[factorySymbol](), p);
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(dummyToFactoryMap.get(target)!(), 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
|
||||
* @param asFunction Mock the proxy as a function
|
||||
* @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 {
|
||||
let cache: T;
|
||||
|
||||
const dummy = asFunction ? function () { } as any : {};
|
||||
dummy[factorySymbol] = () => {
|
||||
return dummy[cacheSymbol] ??= factory();
|
||||
};
|
||||
const proxyFactory = () => cache ??= factory();
|
||||
|
||||
const proxy = new Proxy(dummy, lazyHandler) as T;
|
||||
proxyToFactoryMap.set(proxy, dummy[factorySymbol]);
|
||||
proxyToFactoryMap.set(proxy, proxyFactory);
|
||||
dummyToFactoryMap.set(dummy, proxyFactory);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily destructure an object with all the properties being lazified. This assumes all the properties are either an object or a function
|
||||
* @param factory Factory function which resolves to the object (and caches it)
|
||||
* @param asFunction Mock the proxy as a function
|
||||
* @example
|
||||
*
|
||||
* 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { findExports } from "./finders";
|
|||
import { metroModules, subscribeModule } from "./modules";
|
||||
import type { FilterFn } from "./types";
|
||||
|
||||
const proxyInfoMap = new WeakMap<{}, FindProxyInfo<any[]>>();
|
||||
const proxyContextMap = new WeakMap<any, FindProxyContext<any[]>>();
|
||||
|
||||
interface FindProxyInfo<A extends unknown[]> {
|
||||
interface FindProxyContext<A extends unknown[]> {
|
||||
filter: FilterFn<A>;
|
||||
indexed: boolean;
|
||||
moduleId: number | undefined;
|
||||
|
|
@ -24,8 +24,8 @@ function getIndexedSingleFind<A extends unknown[]>(filter: FilterFn<A>) {
|
|||
return id ? Number(id) : void 0;
|
||||
}
|
||||
|
||||
function subscribeModuleForFind(proxy: any, callback: (exports: any) => void) {
|
||||
const info = getFindProxyInfo(proxy);
|
||||
function subscribeModuleOfFind(proxy: any, callback: (exports: any) => void) {
|
||||
const info = getFindContext(proxy);
|
||||
if (!info) throw new Error("Subscribing a module for non-proxy-find");
|
||||
if (!info.indexed) throw new Error("Attempting to subscribe to a non-indexed find");
|
||||
|
||||
|
|
@ -34,37 +34,39 @@ function subscribeModuleForFind(proxy: any, callback: (exports: any) => void) {
|
|||
});
|
||||
}
|
||||
|
||||
export function getFindProxyInfo<A extends unknown[]>(proxy: any): FindProxyInfo<A> | void {
|
||||
return proxyInfoMap.get(proxy) as unknown as FindProxyInfo<A>;
|
||||
export function getFindContext<A extends unknown[]>(proxy: any): FindProxyContext<A> | void {
|
||||
return proxyContextMap.get(proxy) as unknown as FindProxyContext<A>;
|
||||
}
|
||||
|
||||
export function createFindProxy<A extends unknown[]>(filter: FilterFn<A>) {
|
||||
let cache: any = undefined;
|
||||
|
||||
const moduleId = getIndexedSingleFind(filter);
|
||||
const info: FindProxyInfo<A> = {
|
||||
const context: FindProxyContext<A> = {
|
||||
filter,
|
||||
indexed: !!moduleId,
|
||||
moduleId,
|
||||
getExports(cb: (exports: any) => void) {
|
||||
if (!moduleId || metroModules[moduleId]?.isInitialized) {
|
||||
return cb(findExports(filter)), () => { };
|
||||
return cb(this.unproxy()), () => { };
|
||||
}
|
||||
return this.subscribe(cb);
|
||||
},
|
||||
subscribe(cb: (exports: any) => void) {
|
||||
return subscribeModuleForFind(proxy, cb);
|
||||
return subscribeModuleOfFind(proxy, cb);
|
||||
},
|
||||
get cache() {
|
||||
return cache;
|
||||
},
|
||||
unproxy() {
|
||||
return cache ??= findExports(filter);
|
||||
cache ??= findExports(filter);
|
||||
if (!cache) throw new Error(`${filter.uniq} is ${typeof cache}! (id ${context.moduleId ?? "unknown"})`);
|
||||
return cache;
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = proxyLazy(() => cache ??= findExports(filter));
|
||||
proxyInfoMap.set(proxy, info as FindProxyInfo<any>);
|
||||
const proxy = proxyLazy(() => context.unproxy());
|
||||
proxyContextMap.set(proxy, context as FindProxyContext<any>);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,6 @@ export type FilterCheckDef<A extends unknown[]> = (
|
|||
export interface FilterFn<A extends unknown[]> {
|
||||
(m: any, id: number, defaultCheck: boolean): boolean;
|
||||
filter: FilterCheckDef<A>;
|
||||
args: A;
|
||||
raw: boolean;
|
||||
uniq: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export function createFilterDefinition<A extends unknown[]>(
|
|||
return Object.assign(func, {
|
||||
filter: fn,
|
||||
raw,
|
||||
args,
|
||||
uniq: [
|
||||
raw && "raw::",
|
||||
uniqMaker(args)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue